diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 1e514af..0000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,17 +0,0 @@ -# ⚠️ External Pull Requests Not Accepted - -Thank you for your interest in this project! - -However, **this project does not accept external contributions or pull requests** at this time. Development is managed internally by the PNNL team. - -## Alternative Ways to Help - -If you've found a bug or have a suggestion: - -1. **Report an Issue**: Open a bug report or feature request instead -2. **Share Feedback**: Your feedback helps us improve the library -3. **Use and Share**: Use the library and share your experiences - ---- - -We appreciate your understanding and interest in the COMcheck API Python client! diff --git a/comcheck_api/__init__.py b/comcheck_api/__init__.py index ae96c5c..24cbd19 100644 --- a/comcheck_api/__init__.py +++ b/comcheck_api/__init__.py @@ -58,6 +58,8 @@ from .project_operations import ( project_building_area_operations, project_envelope_operations, + project_exterior_lighting_operations, + project_interior_lighting_operations, ) # Introspection helpers @@ -87,6 +89,8 @@ # Project Operations "project_building_area_operations", "project_envelope_operations", + "project_exterior_lighting_operations", + "project_interior_lighting_operations", # Introspection "list_operations", "lookup_type", diff --git a/comcheck_api/ai/skill/SKILL.md b/comcheck_api/ai/skill/SKILL.md index 4426ed2..2d22aa1 100644 --- a/comcheck_api/ai/skill/SKILL.md +++ b/comcheck_api/ai/skill/SKILL.md @@ -30,17 +30,15 @@ Triggers: `envelope`, `lighting` (which contains `wholeBldgUse[]` — the building areas), `hvac`, `renewable`, and `control` (energy code). No `Project`/`Control` PascalCase aliases exist. - The fields `hvac`, `renewable`, and the **interior-lighting fixtures - inside `activityUse[]`**, plus exterior lighting (`exteriorUse[]`) - and the shared `fixtureSchedule[]`, exist on the model but have - **no operation functions** — leave them at template defaults. Only - `lighting.wholeBldgUse[]` (building areas, including each area's - own `interiorLightingSpace` singleton) is mutable, via - `project_building_area_operations`. -- **Operation modules (functional)**: building areas and envelope - components are added/updated/removed via free functions in - `project_building_area_operations` and `project_envelope_operations`. - Each function takes a `ComBuilding` and returns a new `ComBuilding`. + The fields `hvac`, `renewable`, and the shared `fixtureSchedule[]` + exist on the model but have **no operation functions** — leave them + at template defaults. All other mutable areas have dedicated modules. +- **Operation modules (functional)**: all mutations go through free + functions that take a `ComBuilding` and return a new `ComBuilding`: + - `project_building_area_operations` — `WholeBldgUse` items + - `project_envelope_operations` — roofs, walls, floors, windows, doors, skylights, thermal bridges + - `project_interior_lighting_operations` — `ActivityUse` items (interior lighting spaces + fixtures) + - `project_exterior_lighting_operations` — `ExteriorUse` items + zone type - **Envelope items attach to a building-area key**: every `add_*_to_project` envelope function takes `(project, building_area_key, new_component)`. Look up the key @@ -147,24 +145,18 @@ print(result["performanceRating"]) bypass the validation logic in the operation modules. Always go through `project_envelope_operations` and `project_building_area_operations` instead. -- Don't add, update, or remove interior lighting (the - `activityUse[]` fixtures), exterior lighting (`exteriorUse[]`, - `fixtureSchedule[]`), HVAC/mechanical, or renewable-energy - components — no operations exist for them. The whole-building - `interiorLightingSpace` singleton on each `WholeBldgUse` *is* - editable through `project_building_area_operations`; the per- - activity lighting nested under `activityUse[]` is not. The - `COMcheckClient` user methods (`list_projects`, `get_project`, +- Don't add, update, or remove `fixtureSchedule[]`, HVAC/mechanical, or + renewable-energy components — no operations exist for them yet. + The `COMcheckClient` user methods (`list_projects`, `get_project`, `update_project`, `update_uvalues`, `start_run_simulation`, `get_simulation_status`, `get_simulation_result`, `set_api_key`) - are fully supported and - fine to use. The compliance/report client methods - (`check_UA_compliance`, `check_requirements`, `generate_report`) are - also fully supported. If asked for an unsupported mutation area, - tell the user it's not implemented and offer building-area / - envelope / simulation instead. Confirm operation scope with - `comcheck_api.list_operations()` (only `building_area` and - `envelope` groups exist). + are fully supported and fine to use. The compliance/report client + methods (`check_UA_compliance`, `check_requirements`, + `generate_report`) are also fully supported. If asked for an + unsupported mutation area, tell the user it's not implemented and + offer building-area / envelope / lighting / simulation instead. + `comcheck_api.list_operations()` enumerates the `building_area`, + `envelope`, `interior_lighting`, and `exterior_lighting` groups. ## Common patterns @@ -240,6 +232,79 @@ else: raise TimeoutError(f"Simulation {session_id} did not complete in 5 min") ``` +### Adding interior lighting (ActivityUse + fixtures) + +Interior lighting lives under `wholeBldgUse[i].activityUse[]`. Use +`project_interior_lighting_operations` — there are no fixture-level ops; +edit the `activityUse`'s `interiorLightingSpace.fixture[]` and pass the +whole `activityUse` through `update_interior_lighting_space_in_project`. + +```python +from comcheck_api import project_interior_lighting_operations as il_ops +from comcheck_api.defaults import get_default_interior_lighting_space_template, get_default_fixture_template +from comcheck_api.types.core_types import ActivityTypeOptions + +# fixtureType is the required identifier (a description string); lightingType +# is optional and marked for deprecation, so leave it unset. +fixture = get_default_fixture_template() +fixture.description = "Recessed LED" +fixture.fixtureType = "Recessed LED" +fixture.fixtureWattage = 20.0 +fixture.quantity = 10 + +activity_use = get_default_interior_lighting_space_template() +activity_use.areaDescription = "Open Office" +activity_use.activityType = ActivityTypeOptions.ACTIVITY_COMMON_OFFICE_OPEN +activity_use.interiorLightingSpace = activity_use.interiorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} +) +project = il_ops.add_interior_lighting_space_to_project(project, area_key, activity_use) + +# Update a field — fixtures are preserved unless you also pass interiorLightingSpace +project = il_ops.update_interior_lighting_space_in_project( + project, area_key, "Open Office", {"floorArea": 2500.0} +) + +# Remove +project = il_ops.remove_interior_lighting_space_from_project(project, area_key, "Open Office") +``` + +### Adding exterior lighting (ExteriorUse + zone type) + +Exterior lighting lives under `lighting.exteriorUse[]`. Set a real zone type +first, then add `ExteriorUse` items with fixtures inline. + +```python +from comcheck_api import project_exterior_lighting_operations as el_ops +from comcheck_api.defaults import get_default_exterior_lighting_area_template, get_default_fixture_template +from comcheck_api.types.core_types import ExteriorLightingZoneTypeOptions, ExteriorUseTypeOptions + +# Must set zone type before exterior compliance can be evaluated. +# EXT_ZONE_UNSPECIFIED raises ValueError; a raw string raises TypeError. +project = el_ops.set_exterior_lighting_zone_type_in_project( + project, ExteriorLightingZoneTypeOptions.EXT_ZONE_NEIGHBORHOOD_BUS_DISTRICT +) + +fixture = get_default_fixture_template() +fixture.description = "Parking LED" +fixture.fixtureWattage = 150.0 +fixture.quantity = 8 + +exterior_use = get_default_exterior_lighting_area_template() +exterior_use.areaDescription = "Main Parking Area" +exterior_use.exteriorType = ExteriorUseTypeOptions.EXTERIOR_PARKING_AREA +exterior_use.exteriorLightingSpace = exterior_use.exteriorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} +) +project = el_ops.add_exterior_lighting_area_to_project(project, exterior_use) + +# Adding while zone is EXT_ZONE_UNSPECIFIED emits UserWarning (not an error) +project = el_ops.update_exterior_lighting_area_in_project( + project, "Main Parking Area", {"useQuantity": 6000.0} +) +project = el_ops.remove_exterior_lighting_area_from_project(project, "Main Parking Area") +``` + ### Checking compliance/requirements and generating a report These are synchronous (no polling). All three take a `ComBuilding` diff --git a/comcheck_api/ai/skill/reference/operations.md b/comcheck_api/ai/skill/reference/operations.md index f290618..92ed5eb 100644 --- a/comcheck_api/ai/skill/reference/operations.md +++ b/comcheck_api/ai/skill/reference/operations.md @@ -1,9 +1,11 @@ # Project Operations Reference -Operation functions are free functions in two modules: +Operation functions are free functions in four modules: - `comcheck_api.project_operations.project_building_area_operations` - `comcheck_api.project_operations.project_envelope_operations` +- `comcheck_api.project_operations.project_interior_lighting_operations` +- `comcheck_api.project_operations.project_exterior_lighting_operations` Each function takes a `ComBuilding` and a payload, and returns a new `ComBuilding`. Treat them as immutable transformations. @@ -16,8 +18,8 @@ from comcheck_api import project_building_area_operations as ba_ops | Function | Purpose | |---|---| -| `add_building_area_to_project(project, new_building_area)` | Add a `WholeBldgUse` building area to the project. | -| `update_building_area_in_project(project, building_area_key, updates)` | Update fields of an existing building area by key. | +| `add_building_area_to_project(project, new_building_area)` | Add a `WholeBldgUse` building area to the project. Raises `ValueError` if `areaDescription` already exists. | +| `update_building_area_in_project(project, building_area_key, updates)` | Update fields of an existing building area by key. Raises `ValueError` if the new `areaDescription` collides with another area. | | `remove_building_area_from_project(project, building_area_key)` | Remove a building area by key. | | `get_building_area_keys_from_project(project)` | List `[{key, areaDescription}, …]` for the project. | @@ -33,9 +35,9 @@ a building-area key. Default projects have no areas — add one first: ```python from comcheck_api.defaults import get_default_building_area_template -area = get_default_building_area_template() -area.areaDescription = "Open office" -project = ba_ops.add_building_area_to_project(project, area) +area = get_default_building_area_template() # unique key + areaDescription per call +area.areaDescription = "Open office" # optional override — must be unique within the project +project = ba_ops.add_building_area_to_project(project, area) # raises ValueError if areaDescription already exists area_key = ba_ops.get_building_area_keys_from_project(project)[0]["key"] ``` @@ -100,6 +102,52 @@ roof.orientation = OrientationOptions.UNSPECIFIED_ORIENTATION project = env_ops.add_roof_to_project(project, area_key, roof) ``` +## Interior lighting operations + +```python +from comcheck_api import project_interior_lighting_operations as il_ops +``` + +Interior lighting spaces are `ActivityUse` objects nested under +`lighting.wholeBldgUse[i].activityUse[]`. There are no fixture-level ops — +edit `activityUse.interiorLightingSpace.fixture[]` and pass the whole +`ActivityUse` through `update_interior_lighting_space_in_project`. + +| Function | Purpose | +|---|---| +| `add_interior_lighting_space_to_project(project, building_area_key, new_activity_use)` | Add an `ActivityUse` to a building area. `activityUse.key` is auto-set to `building_area_key`. | +| `update_interior_lighting_space_in_project(project, building_area_key, area_description, updates)` | Update an `ActivityUse` by its `areaDescription`. | +| `remove_interior_lighting_space_from_project(project, building_area_key, area_description)` | Remove an `ActivityUse` by its `areaDescription`. | +| `get_interior_lighting_space_keys_from_project(project, building_area_key)` | List `[{areaDescription, activityType}, …]` for a building area. | + +Use `get_default_interior_lighting_space_template()` as a starting point. +`areaDescription` is the identifier — it is unique within a building area's +`activityUse[]` list and is auto-generated if missing. + +## Exterior lighting operations + +```python +from comcheck_api import project_exterior_lighting_operations as el_ops +``` + +Exterior lighting spaces are `ExteriorUse` objects in +`lighting.exteriorUse[]`. Set a real zone type before exterior compliance +can be evaluated. There are no fixture-level ops — edit +`exteriorUse.exteriorLightingSpace.fixture[]` and pass the whole +`ExteriorUse` through `update_exterior_lighting_area_in_project`. + +| Function | Purpose | +|---|---| +| `set_exterior_lighting_zone_type_in_project(project, zone_type)` | Set `lighting.exteriorLightingZoneType`. Raises `ValueError` for `EXT_ZONE_UNSPECIFIED`, `TypeError` for non-enum values. | +| `add_exterior_lighting_area_to_project(project, new_exterior_lighting_area)` | Add an `ExteriorUse`. Emits `UserWarning` if zone type is still `EXT_ZONE_UNSPECIFIED`. | +| `update_exterior_lighting_area_in_project(project, area_description, updates)` | Update an `ExteriorUse` by its `areaDescription`. | +| `remove_exterior_lighting_area_from_project(project, area_description)` | Remove an `ExteriorUse` by its `areaDescription`. | +| `get_exterior_lighting_area_keys_from_project(project)` | List `[{areaDescription, exteriorType}, …]` for the project. | + +Use `get_default_exterior_lighting_area_template()` as a starting point. +`areaDescription` is the identifier — it is unique within `exteriorUse[]` +and is auto-generated if missing. + ## U-value calculation requires a construction type When `update_uvalues` (or `start_run_simulation`) recalculates assembly diff --git a/comcheck_api/ai/skill/scripts/validate_code.py b/comcheck_api/ai/skill/scripts/validate_code.py index 9712eec..cd40890 100644 --- a/comcheck_api/ai/skill/scripts/validate_code.py +++ b/comcheck_api/ai/skill/scripts/validate_code.py @@ -29,9 +29,11 @@ def _read_input(arg: str) -> str: UNSUPPORTED_PROJECT_ATTRS = {"hvac", "renewable"} +# `wholeBldgUse` (building areas + interior lighting), `activityUse` (interior +# lighting spaces), `exteriorUse` (exterior lighting), and +# `exteriorLightingZoneType` all have operation modules. Only `fixtureSchedule` +# has no operations yet. UNSUPPORTED_LIGHTING_ATTRS = { - "activityUse", - "exteriorUse", "fixtureSchedule", } @@ -79,7 +81,7 @@ def validate(code: str) -> dict: 2. Import check on every imported module name. 3. Scope check that the code only uses operations actually exposed by the SDK and does not mutate the unsupported `hvac`, - `renewable`, or non-`wholeBldgUse` lighting subtrees. + `renewable`, or `lighting.fixtureSchedule` subtrees. """ errors: list[dict] = [] @@ -127,6 +129,8 @@ def validate(code: str) -> dict: if alias.name in { "project_envelope_operations", "project_building_area_operations", + "project_interior_lighting_operations", + "project_exterior_lighting_operations", }: op_module_aliases.add(alias.asname or alias.name) elif isinstance(node, ast.ImportFrom) and node.module == ( @@ -136,6 +140,8 @@ def validate(code: str) -> dict: if alias.name in { "project_envelope_operations", "project_building_area_operations", + "project_interior_lighting_operations", + "project_exterior_lighting_operations", }: op_module_aliases.add(alias.asname or alias.name) @@ -165,7 +171,8 @@ def validate(code: str) -> dict: "line": node.lineno, "message": ( f"`project.lighting.{node.attr}` has no operations; " - "only `lighting.wholeBldgUse[]` is editable." + "edit lighting via the building-area, interior-lighting, " + "and exterior-lighting operation modules instead." ), } ) diff --git a/comcheck_api/client/comcheck_client.py b/comcheck_api/client/comcheck_client.py index 0e1c8a5..30eca6d 100644 --- a/comcheck_api/client/comcheck_client.py +++ b/comcheck_api/client/comcheck_client.py @@ -167,7 +167,7 @@ def update_project( if not old_project: raise COMCheckProjectNotFoundError(project_id) - project_data_json = project_data.model_dump(mode="json", exclude_unset=True) + project_data_json = project_data.model_dump(mode="json") # Preserve user project reference user_project = old_project["userProject"] @@ -262,7 +262,7 @@ def update_uvalues(self, project: ComBuilding) -> ComBuilding: The same ``project`` instance, with u-values updated. """ energy_code = str(project.control.code) - envelope_data = project.envelope.model_dump(mode="json", exclude_unset=True) + envelope_data = project.envelope.model_dump(mode="json") updated_assembly_uvalues = self._service.assemblies_uvalue( envelope_data, energy_code )["data"] @@ -296,7 +296,7 @@ def check_UA_compliance(self, project: ComBuilding) -> Any: Returns: The compliance results payload returned by the API. """ - project_data = project.model_dump(mode="json", exclude_unset=True) + project_data = project.model_dump(mode="json") response = self._service.check_UA_compliance(project_data) return response.get("data") @@ -309,7 +309,7 @@ def check_requirements(self, project: ComBuilding) -> Any: Returns: The requirements payload returned by the API. """ - project_data = project.model_dump(mode="json", exclude_unset=True) + project_data = project.model_dump(mode="json") response = self._service.check_requirements(project_data) return response.get("data") @@ -349,7 +349,7 @@ def generate_report( ``expires``, and ``fileName``. """ report_data = { - "building": project.model_dump(mode="json", exclude_unset=True), + "building": project.model_dump(mode="json"), "envelope": envelope, "extlighting": extlighting, "intlighting": intlighting, @@ -396,7 +396,7 @@ def start_run_simulation( logger.info("Updating project: %s", project_id) project = self.update_project(str(project_id), project) - project_data = project.model_dump(mode="json", exclude_unset=True) + project_data = project.model_dump(mode="json") run_result = self._service.start_run_simulation(project_data) if run_result.data is None: raise COMCheckSimulationError( diff --git a/comcheck_api/constants/envelope_constants.py b/comcheck_api/constants/envelope_constants.py index a033525..bc42250 100644 --- a/comcheck_api/constants/envelope_constants.py +++ b/comcheck_api/constants/envelope_constants.py @@ -50,9 +50,8 @@ DEFAULT_DOOR: Door = Door.model_validate( { - "adjacentSpaceBuildingType": None, "adjacentSpaceType": None, - "allowanceType": None, + "allowanceType": "ENV_ALLOWANCE_NONE", "altExemptType": None, "assemblyType": "Door:Default Door", "bldgUseKey": str(uuid4()), @@ -86,7 +85,7 @@ DEFAULT_SKYLIGHT: Skylight = Skylight.model_validate( { "adjacentSpaceType": None, - "allowanceType": None, + "allowanceType": "ENV_ALLOWANCE_NONE", "assemblyType": "Skylight:Default Skylight", "bldgUseKey": "", "curbType": "NO_CURB_SKYLIGHT", @@ -113,7 +112,7 @@ DEFAULT_ROOF: Roof = Roof.model_validate( { "adjacentSpaceType": None, - "allowanceType": None, + "allowanceType": "ENV_ALLOWANCE_NONE", "assemblyType": "Roof:Default Roof", "bldgUseKey": str(uuid4()), "cavityRValue": 0, @@ -137,7 +136,7 @@ DEFAULT_FLOOR: Floor = Floor.model_validate( { "adjacentSpaceType": None, - "allowanceType": None, + "allowanceType": "ENV_ALLOWANCE_NONE", "altExemptType": None, "assemblyType": "Floor:Default Floor", "bldgUseKey": str(uuid4()), @@ -163,7 +162,7 @@ "adjacentSpaceType": None, "agWallConstructionDetailsType": "NONE", "agWallExteriorFinishDetailsType": None, - "allowanceType": None, + "allowanceType": "ENV_ALLOWANCE_NONE", "assemblyType": "Ext Wall:Default Exterior Wall", "bldgUseKey": str(uuid4()), "cavityRValue": 20, @@ -206,9 +205,8 @@ DEFAULT_BG_WALL: BgWall = BgWall.model_validate( { - "adjacentSpaceBuildingType": None, "adjacentSpaceType": None, - "allowanceType": None, + "allowanceType": "ENV_ALLOWANCE_NONE", "altExemptType": None, "assemblyType": "Basement:Default Basement", "bldgUseKey": "", diff --git a/comcheck_api/constants/exterior_lighting_constants.py b/comcheck_api/constants/exterior_lighting_constants.py new file mode 100644 index 0000000..d4b9f6f --- /dev/null +++ b/comcheck_api/constants/exterior_lighting_constants.py @@ -0,0 +1,25 @@ +"""Exterior lighting constants for COMcheck projects.""" + +from comcheck_api.types.core_types import ( + ExteriorLightingSpace, + ExteriorUse, + ExteriorUseTypeOptions, +) + +DEFAULT_EXTERIOR_LIGHTING_AREA: ExteriorUse = ExteriorUse( + areaDescription="Ext Area 1", # identifier for the exterior area + exteriorType=ExteriorUseTypeOptions.EXTERIOR_PARKING_AREA, + isTradable=True, + powerDensity=0.0, + quantityUnits="sq ft", + useQuantity=1000.0, + exteriorLightingSpace=ExteriorLightingSpace( + description="", + numFixturesAlteredOrAdded=0, + postAltTotalWattage=0.0, + preAltNumberFixtures=0, + preAltTotalWattage=0.0, + altExemptType=None, + fixture=[], + ), +) diff --git a/comcheck_api/constants/interior_lighting_constants.py b/comcheck_api/constants/interior_lighting_constants.py new file mode 100644 index 0000000..75f8b01 --- /dev/null +++ b/comcheck_api/constants/interior_lighting_constants.py @@ -0,0 +1,41 @@ +"""Interior lighting constants for COMcheck projects.""" + +from comcheck_api.types.core_types import ( + ActivityUse, + ActivityTypeOptions, + Fixture, + InteriorLightingSpace, + LightingTypeOptions, +) + +DEFAULT_INTERIOR_LIGHTING_SPACE_AREA: ActivityUse = ActivityUse( + key="__unset__", # key is a placeholder — callers must set it to the parent WholeBldgUse.key + areaDescription="Space 1", # identifier for the ActivityUse within its parent WholeBldgUse + activityType=ActivityTypeOptions.ACTIVITY_COMMON_OFFICE_OPEN, + floorArea=1000.0, + ceilingHeight=9.0, + interiorLightingSpace=InteriorLightingSpace( + altExemptType=None, + description="", + numFixturesAlteredOrAdded=0, + postAltTotalWattage=0.0, + preAltNumberFixtures=0, + preAltTotalWattage=0.0, + allowanceType=None, + exemptionType=None, + allowanceFloorArea=0.0, + rcrFloorToWorkplaneHeight=0.0, + rcrPerimeter=0.0, + rcrWorkplaneToLuminaireHeight=0.0, + fixture=[], + ), +) + +DEFAULT_FIXTURE: Fixture = Fixture( + description="LED fixture", + fixtureType="Fixture 1", + lightingType=LightingTypeOptions.LED, + fixtureWattage=32.0, + quantity=1, + lightingControl=[], +) diff --git a/comcheck_api/defaults.py b/comcheck_api/defaults.py index a4656c9..a5768d7 100644 --- a/comcheck_api/defaults.py +++ b/comcheck_api/defaults.py @@ -4,6 +4,13 @@ from uuid import uuid4 from comcheck_api.constants.building_area_constants import DEFAULT_BUILDING_AREA +from comcheck_api.constants.interior_lighting_constants import ( + DEFAULT_INTERIOR_LIGHTING_SPACE_AREA, + DEFAULT_FIXTURE, +) +from comcheck_api.constants.exterior_lighting_constants import ( + DEFAULT_EXTERIOR_LIGHTING_AREA, +) from comcheck_api.constants.envelope_constants import ( DEFAULT_AG_WALL, DEFAULT_BG_WALL, @@ -35,15 +42,18 @@ def get_default_project_template(): def get_default_building_area_template(): """Return a deep copy of the default :class:`~comcheck_api.types.core_types.WholeBldgUse` template. - Defaults to an *Automotive Facility* with 1 000 sq ft floor area - and interior lighting space initialized. Each call gets a fresh unique - ``key`` so multiple areas can be added to a project without colliding. + Defaults to an *Automotive Facility* with 1 000 sq ft floor area and + interior lighting space initialized. Each call gets a fresh unique ``key`` + and a unique ``areaDescription`` (UUID suffix appended) so multiple areas + can be added to a project without colliding on either field. Returns: A new ``WholeBldgUse`` instance. """ area = copy.deepcopy(DEFAULT_BUILDING_AREA) - area.key = str(uuid4()) + uid = str(uuid4()) + area.key = uid + area.areaDescription = f"{DEFAULT_BUILDING_AREA.areaDescription} {uid[:8]}" return area @@ -143,6 +153,50 @@ def get_default_thermal_bridge_template(): return copy.deepcopy(DEFAULT_THERMAL_BRIDGE) +def get_default_interior_lighting_space_template(): + """Return a deep copy of the default interior lighting space template. + + In the COMcheck API schema, an interior lighting space is represented by + the :class:`~comcheck_api.types.core_types.ActivityUse` model. This + corresponds to what the COMcheck web app calls an *Interior Lighting Space*. + + The ``key`` field is set to ``"__unset__"`` — pass the template directly to + :func:`~comcheck_api.project_operations.project_interior_lighting_operations.add_interior_lighting_space_to_project`, + which sets the key to the parent building area automatically. + + Returns: + A new :class:`~comcheck_api.types.core_types.ActivityUse` instance. + """ + return copy.deepcopy(DEFAULT_INTERIOR_LIGHTING_SPACE_AREA) + + +def get_default_fixture_template(): + """Return a deep copy of the default :class:`~comcheck_api.types.core_types.Fixture` template. + + Defaults to an LED fixture at 32 W, quantity 1, with no lighting controls. + + Returns: + A new ``Fixture`` instance. + """ + return copy.deepcopy(DEFAULT_FIXTURE) + + +def get_default_exterior_lighting_area_template(): + """Return a deep copy of the default exterior lighting area template. + + In the COMcheck API schema, an exterior lighting area is represented by + the :class:`~comcheck_api.types.core_types.ExteriorUse` model. This + corresponds to what the COMcheck web app calls an *Exterior Lighting Area*. + + Defaults to a parking-area exterior lighting area with 1 000 sq ft quantity + and an empty :class:`~comcheck_api.types.core_types.ExteriorLightingSpace`. + + Returns: + A new :class:`~comcheck_api.types.core_types.ExteriorUse` instance. + """ + return copy.deepcopy(DEFAULT_EXTERIOR_LIGHTING_AREA) + + def get_default_fixture_schedule_template(): """Return a default fixture schedule template with a unique key. diff --git a/comcheck_api/introspection.py b/comcheck_api/introspection.py index d30fbba..3e0df6f 100644 --- a/comcheck_api/introspection.py +++ b/comcheck_api/introspection.py @@ -23,11 +23,15 @@ from comcheck_api import ( project_building_area_operations, project_envelope_operations, + project_exterior_lighting_operations, + project_interior_lighting_operations, ) _OP_MODULES = { "building_area": project_building_area_operations, "envelope": project_envelope_operations, + "interior_lighting": project_interior_lighting_operations, + "exterior_lighting": project_exterior_lighting_operations, } diff --git a/comcheck_api/managers/data_manager.py b/comcheck_api/managers/data_manager.py index c45d07a..6ce77e7 100644 --- a/comcheck_api/managers/data_manager.py +++ b/comcheck_api/managers/data_manager.py @@ -195,7 +195,6 @@ def generate_identifier(self, item: T) -> None: or not isinstance(current, str) or current in {getattr(existing, self._identifier, None) for existing in self._data} - or (not current.startswith(self._id_prefix)) ) if not needs_new_identifier: @@ -327,7 +326,7 @@ def modify_one(self, id_value: Any, updates: T | dict[str, Any]) -> T: # This will raise Pydantic ValidationError if types don't align # Get only the model fields to avoid serializing dynamically added methods original_dict = original.model_dump( - mode="python", by_alias=False, exclude_unset=True + mode="python", by_alias=False ) merged = {**original_dict, **updates_dict} try: @@ -357,7 +356,9 @@ def get_model_info(model_class: Type[BaseModel]) -> IdInfo | None: or ``None`` if the class is not registered. """ from comcheck_api.types.core_types import ( + ActivityUse, Door, + ExteriorUse, Roof, Window, BgWall, @@ -369,6 +370,8 @@ def get_model_info(model_class: Type[BaseModel]) -> IdInfo | None: ) MODEL_TO_ID_INFO = { + ActivityUse: IdInfo(identifier="areaDescription", id_prefix="Space"), + ExteriorUse: IdInfo(identifier="areaDescription", id_prefix="Ext Area"), Door: IdInfo(identifier="assemblyType", id_prefix="Door:Door"), Roof: IdInfo(identifier="assemblyType", id_prefix="Roof:Roof"), Window: IdInfo(identifier="assemblyType", id_prefix="Window:Window"), diff --git a/comcheck_api/project_operations/__init__.py b/comcheck_api/project_operations/__init__.py index c296a41..9de6550 100644 --- a/comcheck_api/project_operations/__init__.py +++ b/comcheck_api/project_operations/__init__.py @@ -1,8 +1,15 @@ """Project operations module.""" -from . import project_building_area_operations, project_envelope_operations +from . import ( + project_building_area_operations, + project_envelope_operations, + project_exterior_lighting_operations, + project_interior_lighting_operations, +) __all__ = [ "project_building_area_operations", "project_envelope_operations", + "project_exterior_lighting_operations", + "project_interior_lighting_operations", ] diff --git a/comcheck_api/project_operations/project_building_area_operations.py b/comcheck_api/project_operations/project_building_area_operations.py index 26fe2db..5e3f3f0 100644 --- a/comcheck_api/project_operations/project_building_area_operations.py +++ b/comcheck_api/project_operations/project_building_area_operations.py @@ -5,23 +5,33 @@ from comcheck_api.constants.building_area_constants import DEFAULT_BUILDING_AREA from comcheck_api.types.core_types import ComBuilding, WholeBldgUse -from comcheck_api.utilities.project_utilities import _require_building_area +from comcheck_api.utilities.project_utilities import ( + _require_building_area, + _require_unique_area_description, +) def add_building_area_to_project( project: ComBuilding, new_building_area: WholeBldgUse ) -> ComBuilding: - """Add a new building area to the project using buildingAreaListManager. + """Add a new building area to the project. Args: - project: The project object to modify - new_building_area: The building area object to add + project: The project object to modify. + new_building_area: The building area object to add. Returns: - Updated project object with the building area added + Updated project object with the building area added. + Raises: + ValueError: If another building area with the same ``areaDescription`` + already exists in ``lighting.wholeBldgUse``. """ + desc = getattr(new_building_area, "areaDescription", None) + if desc: + _require_unique_area_description(project, desc) + updated_project = project.model_copy(deep=True) # Ensure interiorLightingSpace is initialized @@ -38,27 +48,47 @@ def add_building_area_to_project( def update_building_area_in_project( project: ComBuilding, building_area_key: str, updates: dict[str, Any] | WholeBldgUse ) -> ComBuilding: - """Update an existing building area in the project using buildingAreaListManager. + """Update an existing building area in the project. Args: - project: The project object to modify - building_area_key: The key of the building area to update - updates: Partial updates (dict) or full building area object to apply + project: The project object to modify. + building_area_key: The ``key`` of the building area to update. + updates: Partial updates (dict) or full building area object to apply. Returns: - Updated project object with the building area updated + Updated project object with the building area updated. + Raises: + ValueError: If ``building_area_key`` is not found, or if ``updates`` + contains an ``areaDescription`` that already belongs to a different + building area in ``lighting.wholeBldgUse``. """ _require_building_area(project, building_area_key) + new_desc = ( + updates.get("areaDescription") + if isinstance(updates, dict) + else getattr(updates, "areaDescription", None) + ) + if new_desc: + _require_unique_area_description( + project, new_desc, exclude_key=building_area_key + ) + updated_project = project.model_copy(deep=True) - updated_project.lighting.update_subcomponent_list(subcomponent_updates=updates, subcomponent_id=building_area_key, subcomponent_name="wholeBldgUse") + updated_project.lighting.update_subcomponent_list( + subcomponent_updates=updates, + subcomponent_id=building_area_key, + subcomponent_name="wholeBldgUse", + ) return updated_project + def remove_building_area_from_project( - project: ComBuilding, building_area_key: str) -> ComBuilding: + project: ComBuilding, building_area_key: str +) -> ComBuilding: """Remove an existing building area in the project. Args: @@ -73,10 +103,13 @@ def remove_building_area_from_project( updated_project = project.model_copy(deep=True) - updated_project.lighting.remove_from_subcomponent_list(subcomponent_id=building_area_key, subcomponent_name="wholeBldgUse") + updated_project.lighting.remove_from_subcomponent_list( + subcomponent_id=building_area_key, subcomponent_name="wholeBldgUse" + ) return updated_project + def get_building_area_keys_from_project(project: ComBuilding) -> list[dict]: """ Extract valid building area identifiers from a COMcheck project. @@ -101,7 +134,11 @@ def get_building_area_keys_from_project(project: ComBuilding) -> list[dict]: return [] return [ - {"key": getattr(area, "key"), "areaDescription": getattr(area, "areaDescription")} + { + "key": getattr(area, "key"), + "areaDescription": getattr(area, "areaDescription"), + } for area in whole_use - if getattr(area, "key", None) is not None and getattr(area, "areaDescription", None) is not None - ] \ No newline at end of file + if getattr(area, "key", None) is not None + and getattr(area, "areaDescription", None) is not None + ] diff --git a/comcheck_api/project_operations/project_exterior_lighting_operations.py b/comcheck_api/project_operations/project_exterior_lighting_operations.py new file mode 100644 index 0000000..607561e --- /dev/null +++ b/comcheck_api/project_operations/project_exterior_lighting_operations.py @@ -0,0 +1,199 @@ +"""Project Exterior Lighting Operations. + +Manages exterior lighting at the ExteriorUse granularity. Each ExteriorUse +carries exactly one (singleton) ExteriorLightingSpace whose fixture[] holds +the fixtures. There are no fixture-level operations — to add, change, or +remove a fixture, edit the ExteriorUse's exteriorLightingSpace.fixture[] list +and pass the whole ExteriorUse through update_exterior_lighting_area_in_project. + +Zone type +--------- +exterior compliance requires a real zone type on lighting.exteriorLightingZoneType +(anything other than EXT_ZONE_UNSPECIFIED). Use +set_exterior_lighting_zone_type_in_project to set it. Adding an ExteriorUse +while the zone is still EXT_ZONE_UNSPECIFIED emits a warning — it is not a +hard error so the project can be built up incrementally. +""" + +import logging +import warnings +from typing import Any + +from comcheck_api.types.core_types import ( + ComBuilding, + ExteriorLightingSpace, + ExteriorLightingZoneTypeOptions, + ExteriorUse, +) +from comcheck_api.utilities.project_utilities import _require_exterior_use + +logger = logging.getLogger(__name__) + + +def set_exterior_lighting_zone_type_in_project( + project: ComBuilding, + zone_type: ExteriorLightingZoneTypeOptions, +) -> ComBuilding: + """Set the project-level exterior lighting zone type. + + Args: + project: The project to modify. + zone_type: An :class:`~comcheck_api.types.core_types.ExteriorLightingZoneTypeOptions` + value. Must not be ``EXT_ZONE_UNSPECIFIED`` — exterior compliance + cannot be evaluated without a real zone type. + + Returns: + Updated project with the zone type set. + + Raises: + TypeError: If zone_type is not an ExteriorLightingZoneTypeOptions member. + ValueError: If zone_type is EXT_ZONE_UNSPECIFIED. + """ + if not isinstance(zone_type, ExteriorLightingZoneTypeOptions): + raise TypeError( + f"zone_type must be an ExteriorLightingZoneTypeOptions member, " + f"got {type(zone_type).__name__!r}." + ) + if zone_type == ExteriorLightingZoneTypeOptions.EXT_ZONE_UNSPECIFIED: + raise ValueError( + "EXT_ZONE_UNSPECIFIED is not a valid zone type — exterior compliance " + "cannot be evaluated without a real zone type. " + "Choose a value from ExteriorLightingZoneTypeOptions other than " + "EXT_ZONE_UNSPECIFIED." + ) + + updated_project = project.model_copy(deep=True) + updated_project.lighting.exteriorLightingZoneType = zone_type + return updated_project + + +def add_exterior_lighting_area_to_project( + project: ComBuilding, + new_exterior_lighting_area: ExteriorUse, +) -> ComBuilding: + """Add a new ExteriorUse (exterior lighting space) to the project. + + Fixtures and the singleton ExteriorLightingSpace are carried inside + new_exterior_lighting_area — populate exteriorLightingSpace.fixture[] before + passing if you want fixtures on creation. + + Emits a :class:`UserWarning` if the project's + ``lighting.exteriorLightingZoneType`` is still ``EXT_ZONE_UNSPECIFIED``, + because exterior compliance cannot be evaluated until a real zone type is + set. Call :func:`set_exterior_lighting_zone_type_in_project` to fix it. + + Args: + project: The project to modify. + new_exterior_lighting_area: The ExteriorUse to add. + + Returns: + Updated project with the new ExteriorUse added. + """ + zone = project.lighting.exteriorLightingZoneType + if zone == ExteriorLightingZoneTypeOptions.EXT_ZONE_UNSPECIFIED: + warnings.warn( + "The project's exterior lighting zone type is EXT_ZONE_UNSPECIFIED. " + "Exterior compliance cannot be evaluated until a real zone type is set. " + "Call set_exterior_lighting_zone_type_in_project() to fix this.", + UserWarning, + stacklevel=2, + ) + + updated_project = project.model_copy(deep=True) + + new_exterior_lighting_area = new_exterior_lighting_area.model_copy(deep=True) + + # Ensure exteriorLightingSpace is initialised + if new_exterior_lighting_area.exteriorLightingSpace is None: + new_exterior_lighting_area = new_exterior_lighting_area.model_copy( + deep=True, + update={ + "exteriorLightingSpace": ExteriorLightingSpace( + description="", + numFixturesAlteredOrAdded=0, + postAltTotalWattage=0.0, + preAltNumberFixtures=0, + preAltTotalWattage=0.0, + altExemptType=None, + fixture=[], + ) + }, + ) + + updated_project.lighting.append_subcomponent(new_exterior_lighting_area) + return updated_project + + +def update_exterior_lighting_area_in_project( + project: ComBuilding, + area_description: str, + updates: dict[str, Any] | ExteriorUse, +) -> ComBuilding: + """Update an existing ExteriorUse. + + To add, change, or remove fixtures: set the desired + exteriorLightingSpace.fixture[] on the updates dict (or the full + ExteriorUse object) before calling this function. + + Args: + project: The project to modify. + area_description: The areaDescription of the ExteriorUse to update. + updates: Partial updates (dict) or full ExteriorUse to apply. + + Returns: + Updated project with the ExteriorUse modified. + """ + _require_exterior_use(project, area_description) + + updated_project = project.model_copy(deep=True) + updated_project.lighting.update_subcomponent_list( + subcomponent_updates=updates, + subcomponent_id=area_description, + subcomponent_name="exteriorUse", + ) + return updated_project + + +def remove_exterior_lighting_area_from_project( + project: ComBuilding, + area_description: str, +) -> ComBuilding: + """Remove an ExteriorUse (and its lighting space + fixtures) from the project. + + Args: + project: The project to modify. + area_description: The areaDescription of the ExteriorUse to remove. + + Returns: + Updated project with the ExteriorUse removed. + """ + _require_exterior_use(project, area_description) + + updated_project = project.model_copy(deep=True) + updated_project.lighting.remove_from_subcomponent_list( + subcomponent_id=area_description, + subcomponent_name="exteriorUse", + ) + return updated_project + + +def get_exterior_lighting_area_keys_from_project(project: ComBuilding) -> list[dict]: + """Return the areaDescription and exteriorType of all ExteriorUse items. + + Args: + project: The project to query. + + Returns: + List of dicts with keys ``areaDescription`` and ``exteriorType`` + for each ExteriorUse in the project. + """ + exterior_uses = project.get_by_path("lighting.exteriorUse") + if not isinstance(exterior_uses, list): + return [] + return [ + { + "areaDescription": getattr(exterior_use, "areaDescription", None), + "exteriorType": getattr(exterior_use, "exteriorType", None), + } + for exterior_use in exterior_uses + ] diff --git a/comcheck_api/project_operations/project_interior_lighting_operations.py b/comcheck_api/project_operations/project_interior_lighting_operations.py new file mode 100644 index 0000000..fddad69 --- /dev/null +++ b/comcheck_api/project_operations/project_interior_lighting_operations.py @@ -0,0 +1,184 @@ +"""Project Interior Lighting Operations. + +Manages interior lighting at the ActivityUse granularity. In the COMcheck +API schema, an interior lighting space is represented by the ``ActivityUse`` +model (``lighting.wholeBldgUse[i].activityUse[]``). Each ActivityUse carries +exactly one (singleton) InteriorLightingSpace whose fixture[] holds the +fixtures. There are no fixture-level operations — to add, change, or remove a +fixture, edit the ActivityUse's interiorLightingSpace.fixture[] list and pass +the whole ActivityUse through update_interior_lighting_space_in_project. +""" + +from typing import Any + +from comcheck_api.constants.interior_lighting_constants import ( + DEFAULT_INTERIOR_LIGHTING_SPACE_AREA, +) +from comcheck_api.types.core_types import ActivityUse, ComBuilding, WholeBldgUse +from comcheck_api.utilities.project_utilities import _require_activity_use + + +def _find_building_area(project: ComBuilding, building_area_key: str) -> WholeBldgUse: + """Return the WholeBldgUse with the given key, or raise.""" + whole_use = project.lighting.wholeBldgUse if project.lighting else [] + area = next( + (area for area in whole_use if area.key == building_area_key), + None, + ) + if area is None: + raise ValueError( + f"Building area key '{building_area_key}' not found in lighting.wholeBldgUse." + ) + return area + + +def add_interior_lighting_space_to_project( + project: ComBuilding, + building_area_key: str, + new_activity_use: ActivityUse, +) -> ComBuilding: + """Add a new interior lighting space (``ActivityUse``) to a building area. + + Each interior lighting space is stored as an ``ActivityUse`` object in the + COMcheck API schema (``lighting.wholeBldgUse[i].activityUse[]``). + + Fixtures and the singleton InteriorLightingSpace are carried inside + new_activity_use — populate interiorLightingSpace.fixture[] before + passing if you want fixtures on creation. The activityUse.key is + automatically set to building_area_key. + + Args: + project: The project to modify. + building_area_key: Key of the WholeBldgUse to add the ActivityUse to. + new_activity_use: The :class:`~comcheck_api.types.core_types.ActivityUse` + to add (represents one interior lighting space in the web app). + Use :func:`~comcheck_api.defaults.get_default_interior_lighting_space_template` + as a starting point. + + Returns: + Updated project with the new ActivityUse added. + """ + updated_project = project.model_copy(deep=True) + + area = _find_building_area(updated_project, building_area_key) + + # Ensure the activityUse.key matches its parent building area key + new_activity_use = new_activity_use.model_copy( + deep=True, update={"key": building_area_key} + ) + + # Ensure interiorLightingSpace is initialized + if new_activity_use.interiorLightingSpace is None: + new_activity_use = new_activity_use.model_copy( + deep=True, + update={ + "interiorLightingSpace": DEFAULT_INTERIOR_LIGHTING_SPACE_AREA.interiorLightingSpace.model_copy( + deep=True + ) + }, + ) + + area.append_subcomponent(new_activity_use) + + return updated_project + + +def update_interior_lighting_space_in_project( + project: ComBuilding, + building_area_key: str, + area_description: str, + updates: dict[str, Any] | ActivityUse, +) -> ComBuilding: + """Update an existing interior lighting space (``ActivityUse``) in a building area. + + To add, change, or remove fixtures: set the desired + interiorLightingSpace.fixture[] on the updates dict (or the full + ActivityUse object) before calling this function. + + Args: + project: The project to modify. + building_area_key: Key of the WholeBldgUse that owns this ActivityUse. + area_description: The ``areaDescription`` of the + :class:`~comcheck_api.types.core_types.ActivityUse` to update. + updates: Partial updates (dict) or a full + :class:`~comcheck_api.types.core_types.ActivityUse` to apply. + + Returns: + Updated project with the ActivityUse modified. + """ + _require_activity_use(project, building_area_key, area_description) + + updated_project = project.model_copy(deep=True) + area = _find_building_area(updated_project, building_area_key) + + area.update_subcomponent_list( + subcomponent_updates=updates, + subcomponent_id=area_description, + subcomponent_name="activityUse", + ) + + return updated_project + + +def remove_interior_lighting_space_from_project( + project: ComBuilding, + building_area_key: str, + area_description: str, +) -> ComBuilding: + """Remove an interior lighting space (``ActivityUse``) and its fixtures from a building area. + + Args: + project: The project to modify. + building_area_key: Key of the WholeBldgUse that owns this ActivityUse. + area_description: The ``areaDescription`` of the + :class:`~comcheck_api.types.core_types.ActivityUse` to remove. + + Returns: + Updated project with the ActivityUse removed. + """ + _require_activity_use(project, building_area_key, area_description) + + updated_project = project.model_copy(deep=True) + area = _find_building_area(updated_project, building_area_key) + + area.remove_from_subcomponent_list( + subcomponent_id=area_description, + subcomponent_name="activityUse", + ) + + return updated_project + + +def get_interior_lighting_space_keys_from_project( + project: ComBuilding, building_area_key: str +) -> list[dict]: + """Return identifying fields for all interior lighting spaces (``ActivityUse`` items) in a building area. + + Args: + project: The project to query. + building_area_key: Key of the WholeBldgUse to query. + + Returns: + List of dicts with keys ``areaDescription`` and ``activityType`` + for each :class:`~comcheck_api.types.core_types.ActivityUse` in the + building area. + """ + whole_use = project.get_by_path("lighting.wholeBldgUse") + if not isinstance(whole_use, list): + return [] + + area = next( + (area for area in whole_use if getattr(area, "key", None) == building_area_key), + None, + ) + if area is None: + return [] + + activity_uses = getattr(area, "activityUse", []) or [] + return [ + { + "areaDescription": getattr(activity_use, "areaDescription", None), + "activityType": getattr(activity_use, "activityType", None), + } + for activity_use in activity_uses + ] diff --git a/comcheck_api/schemas/comCheck.schema.json b/comcheck_api/schemas/comCheck.schema.json index 2099f4a..59146ed 100644 --- a/comcheck_api/schemas/comCheck.schema.json +++ b/comcheck_api/schemas/comCheck.schema.json @@ -8,7 +8,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "control": { @@ -29,23 +32,26 @@ }, "semiheated": { "description": "A boolean to indicate whether the project is a semi-heated type", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] }, "isNonresidentialConditioning": { "description": "Flag to indicate the building has conditional type of non-resindetial conditioning.", - "type": "boolean", + "type": ["boolean", "null"], "default": false, "$comment": "true: non-residential conditioning, false: not non-residential conditioning" }, "isResidentialConditioning": { "description": "Flag to indicate the building has conditional type of resindetial conditioning.", - "type": "boolean", + "type": ["boolean", "null"], "default": false, "$comment": "true: residential conditioning, false: not residential conditioning" }, "isSemiheatedConditioning": { "description": "Flag to indicate the building has conditional type of semiheated conditioning.", - "type": "boolean", + "type": ["boolean", "null"], "default": false, "$comment": "true: semiheated conditioning, false: not semiheated conditioning." }, @@ -56,8 +62,10 @@ }, "performanceRating": { "description": "Appendix C compliance index", - "type": ["number", "null"], - "minimum": 0.0, + "type": [ + "number", + "null" + ], "default": null, "effective_energy_codes": [ "CEZ_90_1_2013", @@ -69,11 +77,17 @@ }, "energyCreditPerformanceRating": { "description": "Appendix C compliance index for energy credit calculation", - "type": ["number", "null"], - "minimum": 0.0, + "type": [ + "number", + "null" + ], "default": null, "$comment": "Applicable to 90.1 2022 for envelope performance energy credit calculation.", - "effective_energy_codes": ["CEZ_IECC2024", "CEZ_90_1_2022", "CEZ_MN"] + "effective_energy_codes": [ + "CEZ_IECC2024", + "CEZ_90_1_2022", + "CEZ_MN" + ] }, "lighting": { "description": "Lighting", @@ -87,6 +101,11 @@ "description": "Renewable systems", "$ref": "comCheck.schema.json#/definitions/Renewable" }, + "bldgUseType": { + "description": "Building Use Type", + "$ref": "comCheck.schema.json#/definitions/BuildingUseTypeOptions", + "$comment": "Legacy enum, only ACTIVITY is valid in the new ComCheck Web" + }, "buildingUseType": { "description": "Building Use Type", "$ref": "comCheck.schema.json#/definitions/BuildingUseTypeOptions", @@ -115,35 +134,58 @@ }, "constructionType": { "description": "Deprecated field, default to 'None'.", - "type": "string" + "type": [ + "string", + "null" + ] }, "allElectric": { "description": "Advanced reporting indicates whether the project is all electric", - "type": ["boolean", "integer", "null"], + "type": [ + "boolean", + "integer", + "null" + ], "default": null, "$comment": "true: all electric, false: otherwise, use integer 0 is false, 1 or above is true" }, "isRenewable": { "description": "Advanced reporting indicates whether the project has on-site renewable system", - "type": ["boolean", "integer", "null"], + "type": [ + "boolean", + "integer", + "null" + ], "default": null, "$comment": "true: has on-site renewable system, false: otherwise, use integer 0 is false, 1 or above is true" }, "hasBattery": { "description": "Advanced reporting indicates whether the project has installed on-site battery", - "type": ["boolean", "integer", "null"], + "type": [ + "boolean", + "integer", + "null" + ], "default": null, "$comment": "true: has onsite battery, false: otherwise, use integer 0 is false, 1 or above is true" }, "hasCharger": { "description": "Advanced reporting indicates whether the project has on-site generator", - "type": ["boolean", "integer", "null"], + "type": [ + "boolean", + "integer", + "null" + ], "default": null, "$comment": "true: has onsite generator, false: otherwise, use integer 0 is false, 1 or above is true" }, "hasHeatPump": { "description": "Advanced reporting indicates whether the building conditioned by heat pumps", - "type": ["boolean", "integer", "null"], + "type": [ + "boolean", + "integer", + "null" + ], "default": null, "$comment": "true: has heat pump, false: otherwise, use integer 0 is false, 1 or above is true" }, @@ -157,26 +199,40 @@ "$ref": "comCheck.schema.json#/definitions/ProjectSubTypeOptions", "default": "CONSTRUCTION_COMPLETE", "$comment": "The availability of sub-type is based on the project type selected.", - "effective_energy_codes": ["CEZ_90_1_2022", "CEZ_IECC2024"] + "effective_energy_codes": [ + "CEZ_90_1_2022", + "CEZ_IECC2024" + ] }, "projectMechanicalType": { "description": "Project mechanical system type", "$ref": "comCheck.schema.json#/definitions/ProjectMechanicalTypeOptions", "default": "PROJECT_HVAC_WITH_CENTRAL", "$comment": "The availability of sub-type is based on the project sub-type selected.", - "effective_energy_codes": ["CEZ_90_1_2022", "CEZ_IECC2024"] + "effective_energy_codes": [ + "CEZ_90_1_2022", + "CEZ_IECC2024" + ] }, "projectCoreAndShellCredit": { "description": "Project credits gained from core and shell", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": null, "$comment": "Only needed when $.projectSubType == CONSTRUCTION_CORE_AND_SHELL", - "effective_energy_codes": ["CEZ_90_1_2022"] + "effective_energy_codes": [ + "CEZ_90_1_2022" + ] }, "feetBldgHeight": { "description": "Building feet height", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": null }, @@ -192,6 +248,29 @@ "$ref": "comCheck.schema.json#/definitions/Requirements" }, "minItems": 0 + }, + "efficiencyPackageType": { + "description": "Efficiency Package Type", + "enum": [ + "EFF_PACKAGE_UNKNOWN", + "EFF_PACKAGE_HVAC_PERFORMANCE", + "EFF_PACKAGE_LIGHTING_REDUCED_LPD", + "EFF_PACKAGE_REDUCED_AIR_INFILTRATION", + "EFF_PACKAGE_ENHANCED_ENVELOPE_PERFORMANCE", + "EFF_PACKAGE_ENHANCED_LIGHTING_CONTROLS", + "EFF_PACKAGE_ONSITE_RENEWABLES", + null + ], + "default": null + }, + "energyCreditMultiplierException": { + "description": "Energy Credit Multiplier Exception", + "enum": [ + "NO_ENERGY_CREDIT_MULTIPLIER_EXCEPTION", + "ENERGY_CREDIT_MULTIPLIER_EXCEPTION_LOW_ENERGY_BUILDINGS", + "ENERGY_CREDIT_MULTIPLIER_EXCEPTION_PRIMARY_HEAT_PUMP", + null + ] } }, "required": [ @@ -213,7 +292,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "category": { @@ -233,12 +315,18 @@ }, "locationOnPlans": { "description": "Requirement Answer - Location On Plans", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "" }, "exceptionName": { "description": "Requirement Answer - Exception Name", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "" } }, @@ -256,19 +344,29 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "version": { "description": "Software version - shall always set to an empty string", - "type": "string", + "type": [ + "string", + "null" + ], "default": "" }, "code": { "description": "Energy code types", "anyOf": [ - { "$ref": "comCheck.schema.json#/definitions/EnergyCodeOptions" }, - { "$ref": "comCheck.schema.json#/definitions/StateRegionEnergyCodeOptions" } + { + "$ref": "comCheck.schema.json#/definitions/EnergyCodeOptions" + }, + { + "$ref": "comCheck.schema.json#/definitions/StateRegionEnergyCodeOptions" + } ] }, "complianceMode": { @@ -278,7 +376,11 @@ "$comment": "For COMcheck API call, this value should always be UA." } }, - "required": ["version", "code", "complianceMode"], + "required": [ + "version", + "code", + "complianceMode" + ], "additionalProperties": false }, "Project": { @@ -286,167 +388,265 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "projectTitle": { "description": "Project title", - "type": ["string"], + "type": [ + "string" + ], "default": "New Project" }, "projectPermitNumber": { "description": "Project permit ID", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectPermitDate": { "description": "Project permit submission date", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectTaxMap": { "description": "Project tax map", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectLotNumber": { "description": "Project lot number", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectAddress": { "description": "Project address - street", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectAddress2": { "description": "Project address - street number", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectCity": { "description": "City", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectState": { "description": "State", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectZipCode": { "description": "Zip code", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "projectComplete": { "description": "Project completion status", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "default": null }, "ownerFirstName": { "description": "Project owner first name", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerLastName": { "description": "Project owner last name", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerCompany": { "description": "Project owner's company", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerAddress": { "description": "Project owner's address - street", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerAddress2": { "description": "Project owner's address - street number", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerCity": { "description": "City", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerState": { "description": "State", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerZipCode": { "description": "Zip code", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerPhone": { "description": "Phone number", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "ownerEmail": { "description": "Email address", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerFirstName": { "description": "Project developer first name", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerLastName": { "description": "Project developer last name", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerCompany": { "description": "Project developer's company", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerAddress": { "description": "Project developer's address - street", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerAddress2": { "description": "Project developer's address - street number", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerCity": { "description": "City", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerState": { "description": "State", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerZipCode": { "description": "Zip code", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerPhone": { "description": "Phone number", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "developerEmail": { "description": "Email address", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "notes": { "description": "$comment", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null } }, @@ -458,7 +658,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "agWall": { @@ -523,7 +726,7 @@ "useOrientationDetails": { "description": "use orientation details for calculation", "type": "boolean", - "default": true, + "const": true, "$comment": "This field shall always set to true" }, "useVltDetails": { @@ -545,7 +748,10 @@ }, "postAltWindowWallPct": { "description": "post alteration window to wall ratio in percentage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "default": null, "minimum": 0.0, "maximum": 100.0, @@ -553,7 +759,10 @@ }, "postAltSkylightRoofPct": { "description": "post alteration skylight to roof ratio in percentage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "default": null, "minimum": 0.0, "maximum": 100.0, @@ -561,7 +770,10 @@ }, "altPctGlazingAreaReplaced": { "description": "Alteration percentage of glazing area", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "default": null, "minimum": 0.0, "maximum": 100.0, @@ -569,7 +781,10 @@ }, "altPctSkylightAreaReplaced": { "description": "Alteration percentage of skylight area", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "default": null, "minimum": 0.0, "maximum": 100.0, @@ -600,7 +815,15 @@ "$comment": "Applicable when $.projectType == ALTERATION" } }, - "required": ["agWall", "bgWall", "roof", "floor", "door", "window", "skylight"], + "required": [ + "agWall", + "bgWall", + "roof", + "floor", + "door", + "window", + "skylight" + ], "additionalProperties": false }, "Location": { @@ -608,7 +831,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "state": { @@ -617,18 +843,27 @@ }, "epwFilename": { "description": "file name", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null, "deprecated": true }, "county": { "description": "county name, spell the full name", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "city": { "description": "city, spell the full name", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "climateZone": { "description": "Climate zone number", @@ -637,7 +872,11 @@ "maximum": 8 } }, - "required": ["state", "city", "climateZone"], + "required": [ + "state", + "city", + "climateZone" + ], "additionalProperties": false }, "AgWall": { @@ -645,12 +884,18 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "The name of the component", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "", "$comment": "This needs to be unique across all ag walls." }, @@ -662,7 +907,10 @@ }, "bldgUseKey": { "description": "key reference of the building use area data group", - "type": "string", + "type": [ + "string", + "null" + ], "$comment": "String of values referencing :WholeBldgUse:" }, "wallType": { @@ -677,46 +925,63 @@ "agWallExteriorFinishDetailsType": { "description": "Above grade wall exterior finish details", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/AgWallExteriorFinishDetailsTypeOptions" } ], "default": null, - "effective_energy_codes": ["CEZ_MAS_STRETCH_IECC2021"] + "effective_energy_codes": [ + "CEZ_MAS_STRETCH_IECC2021" + ] }, "nextToUncondSpace": { "description": "Flag indicates whether the space is next to an unconditioned space", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "default": null }, "insulationPosition": { "description": "Not sure why basement has this data, likely deprecated. Use null", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "otherWallType": { "description": "other wall types", - "$ref": "comCheck.schema.json#/definitions/AgWallOtherTypeOptions", + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/AgWallOtherTypeOptions" + } + ], "default": "NONE", "$comment": "This data should be NONE unless the $.wallType == OTHER_AG_WALL" }, "adjacentSpaceType": { "description": "Space type of the adjacent space", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" + } ], "default": null, "$comment": "This data is used to identify the required maximum U factor in 90.1 energy codes. Be sure to set a not null value in 90.1 energy code" }, "adjacentSpaceBuildingType": { "description": "Building type of the adjacent space", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions" } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions", "$comment": "This data is used for reference - no actual impact on calculations." }, "thermalBridge": { @@ -735,17 +1000,25 @@ "thermalBridgeExceptionType": { "description": "Type of thermal bridge exceptions", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/ThermalBridgeExceptionTypeOptions" } ], "default": null, - "effective_energy_codes": ["CEZ_90_1_2022", "CEZ_IECC2024"] + "effective_energy_codes": [ + "CEZ_90_1_2022", + "CEZ_IECC2024" + ] }, "effectiveUFactor": { "description": "The effective U factor after thermal bridge adjustment", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "default": null, "$comment": "This value is calculated from engine - modify this number does not impact compliance calculation", "effective_energy_codes": [ @@ -756,7 +1029,10 @@ }, "thermalBridgeAdjustmentFactor": { "description": "Thermal bridge adjustment factor", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "default": null, "$comment": "This value is calculated from engine - modify this number does not impact compliance calculation", "effective_energy_codes": [ @@ -767,20 +1043,18 @@ }, "allowanceType": { "description": "allowance type", - "anyOf": [ - { "type": "null" }, - { - "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" - } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions", "$comment": "TODO - need to fill in the background?" }, "cmuType": { "description": "CMU type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/CMUTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/CMUTypeOptions" + } ], "default": null, "$comment": "Only used when $.wallType == MASONRY_AG_WALL || $.wallType == CONCRETE_AG_WALL" @@ -800,15 +1074,21 @@ "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "exemptionType": { "descriptions": "exemption type", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyExemptionOptions" } @@ -819,15 +1099,22 @@ "furringType": { "descriptions": "Type of furring installation, used for mass surfaces", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/FurringTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/FurringTypeOptions" + } ], "default": null, "$comment": "Only used when $.wallType == MASONRY_AG_WALL || $.wallType == CONCRETE_AG_WALL" }, "heatCapacity": { "description": "heat capacity of a mass wall. Used in other mass wall type", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/ft2*F", "minimum": 0.0, "default": 0.0, @@ -856,31 +1143,41 @@ }, "cavityRValue": { "description": "Average insulation R-value in the cavity between two studs.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "continuousRValue": { "description": "Continuous insulation on the above grade wall. Can be exterior or interior or both.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "continuousDeratedRValue": { "description": "Continuous R value derated factor for thermal bridge effect", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, - "default": 0.0, - "effective_energy_codes": ["CEZ_MAS_STRETCH_IECC2021"], + "effective_energy_codes": [ + "CEZ_MAS_STRETCH_IECC2021" + ], "$comment": "This factor is calculaed in the engine based on user inputs and is used for calculating the continuesR value in MAS code." }, "propUValue": { "description": "Proposed thermal transmittance of the above grade wall.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/h-ft2-F", - "minimum": 0.0, "default": 0.0 }, "altExemptType": { @@ -897,9 +1194,11 @@ }, "grossArea": { "description": "gross area", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "ft2", - "minimum": 0.0, "default": 0.0 } }, @@ -936,12 +1235,18 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "The name of the component", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "" }, "assemblyType": { @@ -952,7 +1257,10 @@ }, "bldgUseKey": { "description": "key reference of the building use area data group", - "type": "string", + "type": [ + "string", + "null" + ], "$comment": "String of values referencing :WholeBldgUse:" }, "wallType": { @@ -977,37 +1285,35 @@ "adjacentSpaceType": { "description": "Space type of the adjacent space", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" + } ], "default": null, "$comment": "This data is used to identify the required maximum U factor in 90.1 energy codes. Be sure to set a not null value in 90.1 energy code" }, "adjacentSpaceBuildingType": { "description": "Building type of the adjacent space", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions" } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions", "$comment": "This data is used for reference - no actual impact on calculations." }, "allowanceType": { "description": "allowance type", - "anyOf": [ - { "type": "null" }, - { - "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" - } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions", "$comment": "TODO - need to fill in the background?" }, "cmuType": { "description": "CMU type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/CMUTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/CMUTypeOptions" + } ], "default": null }, @@ -1026,15 +1332,21 @@ "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "exemptionType": { "descriptions": "exemption type", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyExemptionOptions" } @@ -1045,15 +1357,22 @@ "furringType": { "descriptions": "Type of furring installation, used for mass surfaces", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/FurringTypeOptions" } - ], - "default": null, + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/FurringTypeOptions" + } + ], + "default": null, "$comment": "Only used when $.wallType == MASONRY_AG_WALL || $.wallType == CONCRETE_AG_WALL" }, "heatCapacity": { "description": "heat capacity of a mass wall. Used in other mass wall type", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/ft2*F", "minimum": 0.0, "default": 0.0, @@ -1066,7 +1385,10 @@ }, "insulationPosition": { "description": "Not sure why basement has this data, likely deprecated. Use null", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null }, "window": { @@ -1087,23 +1409,29 @@ }, "cavityRValue": { "description": "Average insulation R-value in the cavity between two studs.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "continuousRValue": { "description": "Continuous insulation on the below grade wall. Can be exterior or interior or both.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "propUValue": { "description": "Proposed thermal transmittance of the below grade wall.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/h-ft2-F", - "minimum": 0.0, "default": 0.0 }, "altExemptType": { @@ -1155,35 +1483,44 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "bldgUseKey": { "description": "key reference of the building use area data group", - "type": "string", + "type": [ + "string", + "null" + ], "$comment": "String of values referencing :WholeBldgUse:" }, "description": { "description": "The name of the component", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "" }, "adjacentSpaceType": { "description": "Space type of the adjacent space", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" + } ], "default": null, "$comment": "This data is used to identify the required maximum U factor in 90.1 energy codes. Be sure to set a not null value in 90.1 energy code" }, "adjacentSpaceBuildingType": { "description": "Building type of the adjacent space", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions" } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions", "$comment": "This data is used for reference - no actual impact on calculations." }, "assemblyType": { @@ -1194,14 +1531,19 @@ }, "propUValue": { "description": "Proposed thermal transmittance of the window.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/h-ft2-F", - "minimum": 0.0, "default": 0.0 }, "grossArea": { "description": "gross area", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "ft2", "minimum": 0.0, "default": 0.0 @@ -1220,49 +1562,66 @@ }, "propShgc": { "description": "Proposed solar heat gain coefficient", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "propProjectionFactor": { "description": "Proposed window projection factor", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "frameType": { "description": "Window frame type", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/FenestrationFrameTypeOptions" } - ], + "$ref": "comCheck.schema.json#/definitions/FenestrationFrameTypeOptions", "default": null }, "glazingType": { "description": "Glazing type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/GlazingTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/GlazingTypeOptions" + } ], "default": null }, "propVt": { "descriptions": "Proposed visible transmittance", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": null }, "preAltPropShgc": { "descriptions": "Pre-Alteration solar heat gain coefficient", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "solarType": { "description": "Solar coating type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/SolarTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/SolarTypeOptions" + } ], "default": null }, @@ -1275,92 +1634,129 @@ "glazingMaterialType": { "description": "Glazing material type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/GlazingMaterialTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/GlazingMaterialTypeOptions" + } ], "default": null }, "productType": { "description": "Product Type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WindowProductionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/WindowProductionTypeOptions" + } ], "default": null }, "allowanceType": { "description": "allowance type", - "anyOf": [ - { "type": "null" }, - { - "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" - } - ], - "default": null + "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" }, "exemptionType": { "descriptions": "exemption type", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyExemptionOptions" } ], "default": null }, + "feetAg": { + "description": "Feet above grade", + "type": [ + "number", + "null" + ], + "minimum": 0.0, + "default": null + }, "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "isSiteShading": { "description": "Is the site shaded", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "default": null }, "perfDataType": { "description": "Performance data type option", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/PerfDataTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/PerfDataTypeOptions" + } ], "default": null }, "productId": { "description": "Product ID", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null, "$comment": "Window product ID, used when the perfDataType is PERF_TYPE_NFRC" }, "preAltPropUval": { "descriptions": "Pre-Alteration U-factor", - "type": ["number", "null"], - "minimum": 0.0, - "default": 0.0 + "type": [ + "number", + "null" + ], + "minimum": 0.0 }, "windowOpenType": { "description": "Window open type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WindowOpenTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/WindowOpenTypeOptions" + } ], "default": null }, "cavityRValue": { "descriptions": "Average insulation R-value in the cavity between two studs.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "continuousRValue": { "description": "Continuous insulation on the door. Can be exterior or interior or both.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 } }, @@ -1395,17 +1791,26 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "bldgUseKey": { "description": "key reference of the building use area data group", - "type": "string", + "type": [ + "string", + "null" + ], "$comment": "String of values referencing :WholeBldgUse:" }, "description": { "description": "The name of the component", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "" }, "assemblyType": { @@ -1417,31 +1822,36 @@ "adjacentSpaceType": { "description": "Space type of the adjacent space", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" + } ], "default": null, "$comment": "This data is used to identify the required maximum U factor in 90.1 energy codes. Be sure to set a not null value in 90.1 energy code" }, "adjacentSpaceBuildingType": { "description": "Building type of the adjacent space", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions" } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions", "$comment": "This data is used for reference - no actual impact on calculations." }, "propUValue": { "description": "Proposed thermal transmittance of the window.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/h-ft2-F", - "minimum": 0.0, "default": 0.0 }, "grossArea": { "description": "gross area", - "type": ["null", "number"], + "type": [ + "null", + "number" + ], "unit": "ft2", "minimum": 0.0, "default": null @@ -1459,49 +1869,66 @@ }, "propShgc": { "description": "Proposed solar heat gain coefficient", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "propProjectionFactor": { "description": "Proposed window projection factor", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "frameType": { "description": "Glass door frame type", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/FenestrationFrameTypeOptions" } - ], + "$ref": "comCheck.schema.json#/definitions/FenestrationFrameTypeOptions", "default": null }, "glazingType": { "description": "Glazing type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/GlazingTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/GlazingTypeOptions" + } ], "default": null }, "propVt": { "descriptions": "Proposed visible transmittance", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": null }, "preAltPropShgc": { "descriptions": "Pre-Alteration solar heat gain coefficient", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "solarType": { "description": "Solar coating type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/SolarTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/SolarTypeOptions" + } ], "default": null }, @@ -1514,33 +1941,37 @@ "glazingMaterialType": { "description": "Glazing material type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/GlazingMaterialTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/GlazingMaterialTypeOptions" + } ], "default": null }, "productType": { "description": "Product Type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WindowProductionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/WindowProductionTypeOptions" + } ], "default": null }, "allowanceType": { "description": "allowance type", - "anyOf": [ - { "type": "null" }, - { - "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" - } - ], - "default": null + "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" }, "exemptionType": { "descriptions": "exemption type", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyExemptionOptions" } @@ -1550,72 +1981,104 @@ "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "isSiteShading": { "description": "Is the site shaded", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "default": null }, "perfDataType": { "description": "Performance data type option", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/PerfDataTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/PerfDataTypeOptions" + } ], "default": null }, "productId": { "description": "Product ID", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null, "$comment": "Window product ID, used when the perfDataType is PERF_TYPE_NFRC" }, "preAltPropUval": { "descriptions": "Pre-Alteration U-factor", - "type": ["number", "null"], - "minimum": 0.0, - "default": 0.0 + "type": [ + "number", + "null" + ], + "minimum": 0.0 }, "doorType": { "description": "Door types", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/DoorTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/DoorTypeOptions" + } ], "default": null }, "doorOpenType": { "description": "Door open types", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/DoorOpenTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/DoorOpenTypeOptions" + } ], "default": null }, "doorEntranceType": { "description": "Door entrance types", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/DoorEntranceTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/DoorEntranceTypeOptions" + } ], "default": null }, "cavityRValue": { "descriptions": "Average insulation R-value in the cavity between two studs.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "continuousRValue": { "description": "Continuous insulation on the door. Can be exterior or interior or both.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 } }, @@ -1654,17 +2117,44 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "bldgUseKey": { "description": "key reference of the building use area data group", - "type": "string", + "type": [ + "string", + "null" + ], "$comment": "String of values referencing :WholeBldgUse:" }, + "cavityRValue": { + "description": "Average insulation R-value in the cavity between two studs.", + "type": [ + "number", + "null" + ], + "unit": "h-ft2-F/Btu", + "default": 0.0 + }, + "continuousRValue": { + "description": "Continuous insulation on the skylight. Can be exterior or interior or both.", + "type": [ + "number", + "null" + ], + "unit": "h-ft2-F/Btu", + "default": 0.0 + }, "description": { "description": "The name of the component", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "" }, "assemblyType": { @@ -1676,31 +2166,36 @@ "adjacentSpaceType": { "description": "Space type of the adjacent space", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" + } ], "default": null, "$comment": "This data is used to identify the required maximum U factor in 90.1 energy codes. Be sure to set a not null value in 90.1 energy code" }, "adjacentSpaceBuildingType": { "description": "Building type of the adjacent space", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions" } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions", "$comment": "This data is used for reference - no actual impact on calculations." }, "propUValue": { "description": "Proposed thermal transmittance of the window.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/h-ft2-F", - "minimum": 0.0, "default": 0.0 }, "grossArea": { "description": "gross area", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "ft2", "minimum": 0.0, "default": 0.0 @@ -1724,82 +2219,103 @@ }, "propShgc": { "description": "Proposed solar heat gain coefficient", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "propProjectionFactor": { "description": "Proposed window projection factor", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "frameType": { "description": "Window frame type", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/FenestrationFrameTypeOptions" } - ], + "$ref": "comCheck.schema.json#/definitions/FenestrationFrameTypeOptions", "default": null }, "glazingType": { "description": "Glazing type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/GlazingTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/GlazingTypeOptions" + } ], "default": null }, "propVt": { "descriptions": "Proposed visible transmittance", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": null }, "preAltPropShgc": { "descriptions": "Pre-Alteration solar heat gain coefficient", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0 }, "solarType": { "description": "Solar coating type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/SolarTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/SolarTypeOptions" + } ], "default": null }, "glazingMaterialType": { "description": "Glazing material type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/GlazingMaterialTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/GlazingMaterialTypeOptions" + } ], "default": null }, "productType": { "description": "Product Type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WindowProductionTypeOptions" } - ], - "default": null - }, - "allowanceType": { - "description": "allowance type", - "anyOf": [ - { "type": "null" }, { - "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/WindowProductionTypeOptions" } ], "default": null }, + "allowanceType": { + "description": "allowance type", + "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" + }, "exemptionType": { "descriptions": "exemption type", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyExemptionOptions" } @@ -1809,41 +2325,61 @@ "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "isSiteShading": { "description": "Is the site shaded", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "default": null }, "perfDataType": { "description": "Performance data type option", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/PerfDataTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/PerfDataTypeOptions" + } ], "default": null }, "productId": { "description": "Product ID", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null, "$comment": "Window product ID, used when the perfDataType is PERF_TYPE_NFRC" }, "preAltPropUval": { "descriptions": "Pre-Alteration U-factor", - "type": ["number", "null"], - "minimum": 0.0, - "default": 0.0 + "type": [ + "number", + "null" + ], + "minimum": 0.0 }, "curbType": { "description": "Skylight curb type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/SkylightCurbTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/SkylightCurbTypeOptions" + } ], "default": null } @@ -1878,12 +2414,18 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "The name of the component", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "", "$comment": "This needs to be unique across all ag walls." }, @@ -1895,50 +2437,53 @@ }, "bldgUseKey": { "description": "key reference of the building use area data group", - "type": "string", + "type": [ + "string", + "null" + ], "$comment": "String of values referencing :WholeBldgUse:" }, "adjacentSpaceType": { "description": "Space type of the adjacent space", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" + } ], "default": null, "$comment": "This data is used to identify the required maximum U factor in 90.1 energy codes. Be sure to set a not null value in 90.1 energy code" }, "adjacentSpaceBuildingType": { "description": "Building type of the adjacent space", - "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions" } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/WholeBuildingTypeOptions", "$comment": "This data is used for reference - no actual impact on calculations." }, "allowanceType": { "description": "allowance type", - "anyOf": [ - { "type": "null" }, - { - "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" - } - ], - "default": null, + "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions", "$comment": "TODO - need to fill in the background?" }, "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "exemptionType": { "descriptions": "exemption type", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyExemptionOptions" } @@ -1961,23 +2506,29 @@ }, "cavityRValue": { "descriptions": "Average insulation R-value in the cavity between two studs.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "continuousRValue": { "description": "Continuous insulation on the above grade wall. Can be exterior or interior or both.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", - "minimum": 0.0, "default": 0.0 }, "propUValue": { "description": "Proposed thermal transmittance of the above grade wall.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "Btu/h-ft2-F", - "minimum": 0.0, "default": 0.0 }, "altExemptType": { @@ -1993,7 +2544,10 @@ }, "grossArea": { "description": "gross area", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "ft2", "minimum": 0.0, "default": 0.0 @@ -2001,32 +2555,48 @@ "roofType": { "description": "roof type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/RoofTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/RoofTypeOptions" + } ], "default": null }, "highAlbedoRoofReqType": { "description": "high albedo roof type - this include the albedo method and emeptions", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/HighAlbedoRoofReqTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/HighAlbedoRoofReqTypeOptions" + } ], - "defualt": null + "default": null }, "otherRoofType": { "description": "Roof types when selected Other RoofType - null if it is not Other RoofType", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/OtherRoofTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/OtherRoofTypeOptions" + } ], "default": null }, "roofInsulType": { "description": "roof insulation types", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/RoofInsulationTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/RoofInsulationTypeOptions" + } ], "default": null }, @@ -2052,7 +2622,10 @@ }, "purlinSpacing": { "description": "Roof purlin spacing", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "default": 0.0, "unit": "ft" @@ -2085,12 +2658,18 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "The name of the component", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "assemblyType": { "description": "The type of the component", @@ -2099,30 +2678,36 @@ }, "bldgUseKey": { "description": "key reference of the building use area data group", - "type": "string", + "type": [ + "string", + "null" + ], "$comment": "String of values referencing :WholeBldgUse:" }, "adjacentSpaceType": { "description": "Space type of the adjacent space", "anyOf": [ - { "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" }, - { "type": "null" } + { + "$ref": "comCheck.schema.json#/definitions/AdjacentSpaceTypeOptions" + }, + { + "type": "null" + } ] }, "allowanceType": { "description": "allowance type", - "anyOf": [ - { - "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" - }, - { "type": "null" } - ] + "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyAllowanceTypeOptions" }, "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, @@ -2132,7 +2717,9 @@ { "$ref": "comCheck.schema.json#/definitions/EnvelopeAssemblyExemptionOptions" }, - { "type": "null" } + { + "type": "null" + } ], "$comment": "Used in IECC 2012 only" }, @@ -2142,21 +2729,29 @@ }, "cavityRValue": { "descriptions": "Average insulation R-value in the cavity between two studs.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", "minimum": 0.0 }, "continuousRValue": { "description": "Continuous insulation on the above grade wall. Can be exterior or interior or both.", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", "minimum": 0.0 }, "propUValue": { "description": "Proposed thermal transmittance of the above grade wall.", - "type": "number", - "unit": "Btu/h-ft2-F", - "minimum": 0.0 + "type": [ + "number", + "null" + ], + "unit": "Btu/h-ft2-F" }, "altExemptType": { "description": "alteration exemption type", @@ -2171,7 +2766,10 @@ }, "grossArea": { "description": "gross area", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "ft2", "minimum": 0.0 }, @@ -2181,13 +2779,19 @@ }, "depthOfInsulation": { "description": "depth of insulation, it only works on a certain numbers including 1, 2, 3, 4, 93, 94, 95 ,96, 97, 98, 99. Mapping is in the $comment", - "type": ["null", "integer"], + "type": [ + "null", + "integer" + ], "minimum": 0, "$comment": "1 -> 1ft, 2 -> 2ft, 3 -> 3ft, 4 -> 4ft, 93 -> Fully insulated, 94 -> Fully Insulated (user specified perimeter R-value + R-3.5 under slab), 95 -> Fully Insulated (user specified perimeter R-value + R-5.0 under slab), 96 -> Fully Insulated (user specified perimeter R-value + R-7.5 under slab), 97 -> Fully Insulated (user specified perimeter R-value + R-10.0 under slab, 98 ->Fully Insulated (user specified perimeter R-value + R-15.0 under slab), 99 -> Fully Insulated (user specified perimeter R-value + R-20.0 under slab)" }, "slabFullInsulBelowMinRValue": { "description": "Full insulation R value below the slab -> the number if fixed based on the selection of depthOfInsulation", - "type": "number", + "type": [ + "number", + "null" + ], "unit": "h-ft2-F/Btu", "minimum": 0.0, "$comment": ", 93 -> 0.0, 94 -> 3.5, 95 -> 5.0, 96 -> 7.5, 97 -> 10.0, 98 -> 15.0, 99 -> 20.0" @@ -2198,24 +2802,37 @@ { "$ref": "comCheck.schema.json#/definitions/SlabInsulationPositionOptions" }, - { "type": "null" } + { + "type": "null" + } ] }, "hasEdgeInsul": { "description": "A boolean to indicate whether the bldg use area uses edge insulation", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "$comment": "user shall provide this data" }, "floorExposedFrameType": { "description": "Floor Exposed Frame type", "anyOf": [ - { "$ref": "comCheck.schema.json#/definitions/FloorExposedFrameType" }, - { "type": "null" } + { + "$ref": "comCheck.schema.json#/definitions/FloorExposedFrameType" + }, + { + "type": "null" + } ], "$comment": "user shall provide this data" } }, - "required": ["bldgUseKey", "description", "assemblyType"], + "required": [ + "bldgUseKey", + "description", + "assemblyType" + ], "additionalProperties": false }, "Lighting": { @@ -2223,7 +2840,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "wholeBldgUse": { @@ -2252,7 +2872,11 @@ } } }, - "required": ["exteriorLightingZoneType", "wholeBldgUse", "exteriorUse"], + "required": [ + "exteriorLightingZoneType", + "wholeBldgUse", + "exteriorUse" + ], "additionalProperties": true }, "HVAC": { @@ -2260,7 +2884,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "hvacSystem": { @@ -2278,14 +2905,22 @@ } }, "fanSystem": { - "description": "fan system", - "type": "array", + "description": "Fan system", + "type": [ + "array", + "null" + ], "items": { "$ref": "comCheck.schema.json#/definitions/FanSystem" - } + }, + "default": null } }, - "required": ["hvacSystem", "hvacPlant", "fanSystem"], + "required": [ + "hvacSystem", + "hvacPlant", + "fanSystem" + ], "additionalProperties": false }, "WholeBldgUse": { @@ -2293,7 +2928,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "areaDescription": { @@ -2302,27 +2940,39 @@ }, "floorArea": { "description": "Whole building use floor area", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2", "$comment": "User shall provide this data" }, "ceilingHeight": { "description": "Average ceiling height", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft" }, "powerDensity": { "description": "Internal equipment power density", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt/ft2", "$comment": "Engine calculated value" }, "internalLoad": { "description": "Internal equipment load", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "Engine calculated value" @@ -2330,32 +2980,53 @@ "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "allowedWattage": { "description": "Allowed wattage", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt" }, "proposedWattage": { "description": "Proposed wattage", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "user shall provide this data" }, "interiorLightingSpace": { "description": "Interior lighting space definition", - "$ref": "comCheck.schema.json#/definitions/InteriorLightingSpace", + "anyOf": [ + { + "$ref": "comCheck.schema.json#/definitions/InteriorLightingSpace" + }, + { + "type": "null" + } + ], "$comment": "This data is deprecated - however, it needs to be kept here for legacy code processing" }, "key": { "description": "Unique identifier for this building use area", - "type": ["string", "number", "null"] + "type": [ + "string", + "number", + "null" + ] }, "wholeBldgType": { "description": "Whole building type", @@ -2371,11 +3042,19 @@ }, "isTenantSpace": { "description": "A boolean to indicate whether the bldg use area is designed for tenant spaces", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "$comment": "Primarily used for IECC 2021 and the state codes based on this version of national code." } }, - "required": ["wholeBldgType", "activityUse", "key", "interiorLightingSpace"], + "required": [ + "wholeBldgType", + "activityUse", + "key", + "interiorLightingSpace" + ], "additionalProperties": true }, "ActivityUse": { @@ -2383,7 +3062,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "key": { @@ -2396,27 +3078,39 @@ }, "floorArea": { "description": "Whole building use floor area", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2", "$comment": "User shall provide this data" }, "ceilingHeight": { "description": "Average ceiling height", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft" }, "powerDensity": { "description": "Internal equipment power density", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt/ft2", "$comment": "Engine calculated value" }, "internalLoad": { "description": "Internal equipment load", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "Engine calculated value" @@ -2424,27 +3118,44 @@ "constructionType": { "description": "Construction types - compliance code specification", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ConstructionTypeOptions" + } ], "$comment": "TODO - verify if this is still needed?" }, "allowedWattage": { "description": "Allowed wattage", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt" }, "proposedWattage": { "description": "Proposed wattage", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "user shall provide this data" }, "interiorLightingSpace": { "description": "Interior lighting space definition", - "$ref": "comCheck.schema.json#/definitions/InteriorLightingSpace" + "anyOf": [ + { + "$ref": "comCheck.schema.json#/definitions/InteriorLightingSpace" + }, + { + "type": "null" + } + ] }, "activityType": { "description": "activity type", @@ -2452,15 +3163,27 @@ }, "roomCavityRatioThreshold": { "description": "Room Cavity Ratio threshold", - "type": ["number", "null"] + "type": [ + "number", + "null" + ] }, "isUnfinishedSpace": { "description": "A flag to indicate whether the space is unfinished or not.", - "type": ["boolean", "null"], - "effective_energy_codes": ["CEZ_IECC2024", "CEZ_IECC2021"] + "type": [ + "boolean", + "null" + ], + "effective_energy_codes": [ + "CEZ_IECC2024", + "CEZ_IECC2021" + ] } }, - "required": ["key", "interiorLightingSpace"], + "required": [ + "key", + "interiorLightingSpace" + ], "additionalProperties": true }, "ExteriorUse": { @@ -2468,7 +3191,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "areaDescription": { @@ -2485,26 +3211,44 @@ }, "powerDensity": { "description": "Internal equipment power density", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt/ft2", "$comment": "Engine calculated value" }, "quantityUnits": { "description": "Quantity units", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "$comment": "This string shows one of the units - typically ft2 or ft." }, "useQuantity": { "description": "The take-off quantity of the exterior use", - "type": "number" + "type": [ + "number", + "null" + ] }, "exteriorLightingSpace": { "description": "Exterior lighting space", - "$ref": "comCheck.schema.json#/definitions/ExteriorLightingSpace" + "anyOf": [ + { + "$ref": "comCheck.schema.json#/definitions/ExteriorLightingSpace" + }, + { + "type": "null" + } + ] } }, - "required": ["exteriorLightingSpace"], + "required": [ + "exteriorLightingSpace" + ], "additionalProperties": false }, "InteriorLightingSpace": { @@ -2512,36 +3256,53 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "Description of the space", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "$comment": "It shall be unique in the project" }, "preAltNumberFixtures": { "description": "Number of fixtures to be altered", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0, "$comment": "This field only used in alteration project - set to 0 if the project type is not alteration" }, "numFixturesAlteredOrAdded": { "description": "Number of fixtures added or altered", - "type": ["integer", "null"], - "minimum": 0, + "type": [ + "integer", + "null" + ], "$comment": "This field only used in alteration project - set to 0 if the project type is not alteration" }, "preAltTotalWattage": { "description": "Total wattage before the alteration", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "This field only used in alteration project - set to 0.0 if the project type is not alteration" }, "postAltTotalWattage": { "description": "Total wattage after the alteration", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "This field only used in alteration project - set to 0.0 if the project type is not alteration" @@ -2559,63 +3320,96 @@ }, "exemptionType": { "description": "deprecated, use null", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "allowanceType": { "description": "deprecated, use null", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "allowanceFloorArea": { "description": "The floor area that covered by allowance", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, "rcrPerimeter": { "description": "perimeter", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft" }, "rcrFloorToWorkplaneHeight": { "description": "Floor-to-workplane height", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft" }, "rcrWorkplaneToLuminaireHeight": { "description": "Workplane-to-luminaire height", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft" }, "primaryDaylight": { "description": "Daylighting on the primary sidelight area", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, "secondaryDaylight": { "description": "Daylighting on the secondary sidelight area", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, "skylightToplight": { "description": "Daylighting on the skylight toplight area", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, "roofMonitorToplight": { "description": "Daylighting on the roof monitor top light area", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, "decorativeArea": { "description": "The floor area that covered by decorative lightings", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, @@ -2635,36 +3429,54 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "Description of the space", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "$comment": "It shall be unique in the project" }, "preAltNumberFixtures": { "description": "Number of fixtures to be altered", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0, "$comment": "This field only used in alteration project - set to 0 if the project type is not alteration" }, "numFixturesAlteredOrAdded": { "description": "Number of fixtures added or altered", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0, "$comment": "This field only used in alteration project - set to 0 if the project type is not alteration" }, "preAltTotalWattage": { "description": "Total wattage before the alteration", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "This field only used in alteration project - set to 0.0 if the project type is not alteration" }, "postAltTotalWattage": { "description": "Total wattage after the alteration", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "This field only used in alteration project - set to 0.0 if the project type is not alteration" @@ -2696,47 +3508,48 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "advControlsAllowanceAperture": { "description": "Advanced controls allowance aperture", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "$comment": "Applies to advanced controls allowance type: aperture." }, - "advControlAllowanceType": { - "description": "Advanced control allowance type", - "$ref": "comCheck.schema.json#/definitions/AdvancedControlsAllowanceTypeOptions" + "advControlsAllowanceType": { + "description": "Advanced controls allowance type", + "$ref": "comCheck.schema.json#/definitions/AdvancedControlsAllowanceTypeOptions", + "default": null }, "allowanceFloorArea": { "description": "Floor area covered by the allowance", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "allowanceType": { "description": "Lighting allowance type", - "anyOf": [ - { - "$ref": "comCheck.schema.json#/definitions/LightingAllowanceTypeOptions" - }, - { "type": "null" } - ] + "$ref": "comCheck.schema.json#/definitions/LightingAllowanceTypeOptions" }, "ballast": { "description": "Ballast type", - "anyOf": [ - { - "$ref": "comCheck.schema.json#/definitions/BallastTypeOptions" - }, - { - "type": "null" - } - ] + "$ref": "comCheck.schema.json#/definitions/BallastTypeOptions" }, "description": { "description": "Description of the fixture", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "$comment": "It shall be unique in the project" }, "exemptionType": { @@ -2752,18 +3565,24 @@ }, "fixtureType": { "description": "This field temporarily used to describe the fixture.", - "type": "string", + "type": ["string", "null"], "$comment": "It shall be unique among all fixtures in the interior lighting space." }, "fixtureWattage": { "description": "fixture wattage", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt" }, "lampType": { "description": "deprecated, use null", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "lightingType": { "description": "lighting fixture type", @@ -2772,75 +3591,112 @@ }, "numberOfLamps": { "description": "deprecated, use null", - "type": ["number", "null"] + "type": [ + "number", + "null" + ] }, "powerAllowance": { "description": "Advanced control power allowance", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt" }, "quantity": { "description": "Quantity of the fixture", - "type": "integer", + "type": [ + "integer", + "null" + ], "minimum": 0 }, "quantityWithAdvControls": { "description": "Quantity of the fixture that has advanced control", - "type": "integer", + "type": [ + "integer", + "null" + ], "minimum": 0 }, + "scheduleFixtureKey": { + "description": "UUID to identify this fixture schedule.", + "type": [ + "string", + "null" + ] + }, "trackCircuitBreakerAmps": { "description": "Track lighting circuit breaker amps", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "amps", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackCircuitBreakerVolts": { "description": "Track lighting circuit breaker voltage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "volts", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackCurrentLimiterWattage": { "description": "Track current limiter wattage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackLength": { "description": "Track lighting length", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackTotalLuminaireWattage": { "description": "Track lighting total luminaire wattage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackTransformerWattage": { "description": "Track lighting former wattage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "amps", "$comment": "Used when lightingType is set to TRACK_LIGHTING and the project type set to Alteration" }, "trackLightingWattageBasisType": { "description": "Track lighting wattage basis type", - "anyOf": [ - { - "$ref": "comCheck.schema.json#/definitions/TrackLightingWattageBasisTypeOptions" - }, - { - "type": "null" - } + "$ref": "comCheck.schema.json#/definitions/TrackLightingWattageBasisTypeOptions" + }, + "typeOfFixture": { + "description": "Type of the fixture", + "type": [ + "string", + "null" ] }, "lightingControl": { @@ -2851,7 +3707,12 @@ } } }, - "required": ["description", "quantity", "lightingType", "lightingControl"], + "required": [ + "description", + "quantity", + "fixtureType", + "lightingControl" + ], "additionalProperties": true }, "FixtureSchedule": { @@ -2859,11 +3720,17 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": "integer" + "type": [ + "string", + "integer" + ] }, "lightingId": { "description": "Scope-unique reference identifier for instances of the parent lighting group.", - "type": "integer" + "type": [ + "string", + "integer" + ] }, "scheduleFixtureKey": { "description": "UUID to identify this fixture schedule.", @@ -2871,7 +3738,10 @@ }, "description": { "description": "Description of the fixture schedule", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "fixtureType": { "description": "This field temporarily used to describe the fixture schedule.", @@ -2880,7 +3750,10 @@ }, "fixtureWattage": { "description": "fixture wattage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt" }, @@ -2891,59 +3764,75 @@ }, "trackCircuitBreakerAmps": { "description": "Track lighting circuit breaker amps", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "amps", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackCircuitBreakerVolts": { "description": "Track lighting circuit breaker voltage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "volts", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackCurrentLimiterWattage": { "description": "Track current limiter wattage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackLength": { "description": "Track lighting length", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackTotalLuminaireWattage": { "description": "Track lighting total luminaire wattage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "Used when lightingType is set to TRACK_LIGHTING" }, "trackTransformerWattage": { "description": "Track lighting former wattage", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "amps", "$comment": "Used when lightingType is set to TRACK_LIGHTING and the project type set to Alteration" }, "trackLightingWattageBasisType": { "description": "Track lighting wattage basis type", - "anyOf": [ - { - "$ref": "comCheck.schema.json#/definitions/TrackLightingWattageBasisTypeOptions" - }, - { - "type": "null" - } - ] + "$ref": "comCheck.schema.json#/definitions/TrackLightingWattageBasisTypeOptions" } }, - "required": ["description", "fixtureType", "fixtureWattage", "lightingType", "scheduleFixtureKey"], + "required": [ + "description", + "fixtureType", + "fixtureWattage", + "scheduleFixtureKey" + ], "additionalProperties": true }, "HVACSystem": { @@ -2951,7 +3840,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "altExemptType": { @@ -2972,7 +3864,10 @@ }, "constVolMixingBox": { "description": "Flag to identify whether the HVAC system has constant volume mixing box (deprecated, set to false).", - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "default": false }, "coolingEquipCapacity": { @@ -2989,7 +3884,10 @@ }, "description": { "description": "unique name of the HVAC system", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": "" }, "descriptionCoolEquip": { @@ -3034,7 +3932,10 @@ }, "fanSystemKey": { "description": "Fan system key", - "type": ["null", "string"], + "type": [ + "null", + "string" + ], "default": null }, "fuel": { @@ -3048,13 +3949,18 @@ { "$ref": "comCheck.schema.json#/definitions/SpaceHeatingSystemExceptionOptions" }, - { "type": "null" } + { + "type": "null" + } ], "$comment": "Only effective and required if the energy code == CEZ_CO_DENVER_IECC2021" }, "heatingEquipCapacity": { "description": "Heating equipment capacity", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "kBtu/hr", "default": 0.0 @@ -3069,85 +3975,146 @@ }, "hydronicReheat": { "description": "Flag to identify whether the HVAC has a hydronic reheat system, (deprecated, set to false)", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] }, "isHeatingSysWeatherized": { "description": "Flag to identify whether the heating system is weatherized, (deprecated, set to false)", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] }, "perimeterSystem": { "description": "Flag to identify whether the HVAC system is used to condition perimeter zones", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] }, "propCoolingEquipEfficiencyPartial": { "description": "Proposed system cooling equipment part load efficiency", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "propCoolingEquipEfficiency": { "description": "Proposed system cooling equipment efficiency", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "propHeatingEquipEfficiency": { "description": "Proposed system heating equipment efficiency", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "quantity": { "description": "Quantity of HVAC system", - "type": ["integer", "null"], - "minimum": 1 + "type": [ + "integer", + "null" + ], + "minimum": 0 }, "quantityCoolEquip": { "description": "Quantity of the cooling equipment", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0 }, "quantityHeatEquip": { "description": "Quantity of the heating equipment", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0 }, "reheatRecoolCoil": { "description": "Flag to identify whether the coils are reheat and recooled (deprecated, set to false)", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] + }, + "requirementAnswer": { + "type": "array", + "items": { + "$ref": "comCheck.schema.json#/definitions/Requirements" + }, + "default": [] }, "returnFanHp": { "description": "Return fan HP (deprecated, set to 0)", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "steamReheat": { "description": "Flag to identify whether the HVAC system is steam reheated (deprecated, set to false)", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] }, "supplyFanHp": { "description": "Supply fan HP (deprecated, set to 0)", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "supplyStaticPressure": { "description": "Supply static pressure (deprecated, set to 0)", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "systemType": { "description": "System type (deprecated, set to HVAC)", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "totalFanHp": { "description": "Total fan HP (deprecated, set to 0)", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "varAirBox": { "description": "VAR Air Box", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] }, "varAirVolMixingBox": { "description": "VAR air volume mixing box", - "type": ["boolean", "null"] + "type": [ + "boolean", + "null" + ] }, "zone": { "description": "HVAC zone layout", @@ -3162,7 +4129,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "altExemptType": { @@ -3194,13 +4164,19 @@ }, "condenserFlowRate": { "description": "Condenser flow rate - this data comes from engine. User change this data does not have impact on compliance check", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "gpm/ton" }, "condenserLeavingTemperature": { "description": "leaving water temperature - this data comes from engine. User change this data does not have impact on compliance check", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "F" }, @@ -3210,23 +4186,35 @@ }, "coolingPlantCapacity": { "description": "Cooling plant capacity", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "tons" }, "description": { "description": "Plant unique name", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "enteringCondenserWaterTemperature": { "description": "Condenser entering water temperature - this data comes from engine. User change this data does not have impact on compliance check", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "F" }, "evaporatorLeavingTemperature": { "description": "Evaporator leaving water temperature - this data comes from engine. User change this data does not have impact on compliance check", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "F" }, @@ -3236,7 +4224,10 @@ }, "heatingPlantCapacity": { "description": "Heating Plant Capacity", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "kBtu/h" }, @@ -3254,11 +4245,17 @@ }, "heatRecovery": { "description": "Flag indicates whether the system has heat recovery feature", - "type": "boolean" + "type": [ + "boolean", + "null" + ] }, "heatPumpSimultaneousCoolingAndHeating": { "description": "Flag indicates whether the heat pump can do simultaneous cooling and heating", - "type": "boolean" + "type": [ + "boolean", + "null" + ] }, "heatRejection": { "description": "Heat rejection types", @@ -3266,7 +4263,10 @@ }, "leavingChilledWaterTemperature": { "description": "Leaving chiller water temperature - this data comes from engine. User change this data does not have impact on compliance check", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "F" }, @@ -3276,35 +4276,53 @@ }, "propCoolingPlantEfficiencyPartial": { "description": "Proposed cooling plant part load efficiency", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "propCoolingPlantEfficiency": { "description": "Proposed cooling plant efficiency", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "propHeatingPlantEfficiency": { "description": "Proposed heating plant efficiency", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0 }, "quantity": { "description": "Quantity of the plant system", "type": "integer", - "minimum": 1.0 + "minimum": 0 }, "systemType": { "description": "Deprecated, system type", - "type": "string" + "type": [ + "string", + "null" + ] }, "twoPipeSystem": { "description": "Flag identifies if the plant system is a two pipe system", - "type": "boolean" + "type": [ + "boolean", + "null" + ] }, "waterloopHeatPump": { "description": "Flag identifies if the plant system is a water loop heat pump", - "type": "boolean" + "type": [ + "boolean", + "null" + ] }, "compliancePath": { "description": "Compliance path", @@ -3320,8 +4338,17 @@ "efficiencyRequirementException": { "description": "Natural gas boiler efficiency requirement exceptions", "$ref": "comCheck.schema.json#/definitions/EquipmentEfficiencyRequirementExceptionOptions", - "default": "EFF_EXCEPTION_UNSPECIFIED", - "effective_energy_codes": ["CEZ_90_1_2022", "CEZ_IECC2024"] + "effective_energy_codes": [ + "CEZ_90_1_2022", + "CEZ_IECC2024" + ] + }, + "requirementAnswer": { + "type": "array", + "items": { + "$ref": "comCheck.schema.json#/definitions/Requirements" + }, + "default": [] } }, "additionalProperties": false @@ -3331,16 +4358,26 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "complianceMessage": { "description": "Compliance message calculated from engine", - "type": ["null", "string"] + "type": [ + "null", + "string" + ] }, "complies": { "description": "Flag indicate whether the fan system complies or fail", - "type": ["null", "boolean", "integer"] + "type": [ + "null", + "boolean", + "integer" + ] }, "complyMethod": { "description": "Fan system compliance method", @@ -3348,11 +4385,17 @@ }, "description": { "description": "Unique name of this fan system", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "description2": { "description": "Number of areas served", - "type": "string" + "type": [ + "string", + "null" + ] }, "fan": { "description": "fans", @@ -3363,11 +4406,18 @@ }, "hasPressureDropCredits": { "description": "Flag indicates if the fan system has pressure drop credits", - "type": ["boolean", "integer"] + "enum": [ + 0, + 1, + null + ] }, "fanSystemKey": { "description": "Fan system key, used when reference a fan system in an HVAC system", - "type": "string" + "type": [ + "string", + "null" + ] }, "pressureDropCredits": { "description": "Pressure drop credits", @@ -3378,7 +4428,11 @@ }, "servesAllowanceAreaWithFlowControl": { "description": "Flag indicates if the fan system serves allowance area with flow control", - "type": ["null", "boolean", "integer"], + "type": [ + "null", + "boolean", + "integer" + ], "$comment": "integer 0 or 1, which 0 means false, and 1 mean true" } }, @@ -3390,7 +4444,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "brakeHp": { @@ -3401,7 +4458,10 @@ }, "description": { "description": "Unique name of the fan", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "designBrakeHp": { "description": "Fan design brake HP", @@ -3411,7 +4471,10 @@ }, "fanDesignEfficiency": { "description": "Fan design efficiency", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 100.0, "unit": "%" @@ -3435,19 +4498,23 @@ }, "maxNameplateHp": { "description": "Maximum name plate HP - this number shall be calculated by engine", - "type": "number", - "minimum": 0.0, + "type": [ + "number", + "null" + ], "unit": "HP" }, "nameplateHp": { "description": "Name plate HP", "type": "number", - "minimum": 0.0, "unit": "HP" }, "totalFanEfficiency": { "description": "Peak fan efficiency", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 100, "unit": "%" @@ -3467,7 +4534,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "cfm": { @@ -3488,13 +4558,18 @@ }, "recoveryEffectiveness": { "description": "Energy recovery effectiveness", - "type": "number", - "minimum": 0.0, - "maximum": 1.0 + "type": [ + "number", + "null" + ], + "minimum": 0.0 }, "verticalDuctLength": { "description": "Vertical duct length", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft" }, @@ -3503,7 +4578,9 @@ "$ref": "comCheck.schema.json#/definitions/PressureDropTypeOptions" } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": false }, "ServiceWaterHeatingSystem": { @@ -3511,28 +4588,44 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "circulationPump": { "description": "Flag identifies whether the SWH has a circulation pump", - "type": "boolean" + "type": "boolean", + "default": false }, "heatTraceTapeInstalled": { "description": "Flag identifies whether the SWH has heat trace tape installed", - "type": "boolean" + "type": "boolean", + "default": false }, "combinedSystem": { "description": "Flag identifies whether the SWH is a combined system", - "type": "boolean" + "type": "boolean", + "default": false }, "poolSystem": { "description": "Flag identifies whether the SWH is part of pool system", - "type": "boolean" + "type": "boolean", + "default": false }, - "heatpumpPoolHeater": { + "heatPumpPoolHeater": { "description": "Flag identifies whether the SWH uses heat pump to heat the pool. - Only used when poolSystem is true. False as default", - "type": "boolean" + "type": [ + "boolean", + "null" + ], + "enum": [ + 0, + 1, + null + ], + "default": null }, "inputRating": { "description": "Water heater rated input power. kBtu/h if fuel type is gas or oil, kW if fuel type is electric", @@ -3557,11 +4650,14 @@ "quantity": { "description": "Quantity of the water heater", "type": "integer", - "minimum": 1 + "minimum": 0 }, "description": { "description": "Unique name describes the SWH system", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "systemType": { "description": "Deprecated - set to Water Heater", @@ -3589,20 +4685,31 @@ "swhSystemSubType": { "description": "Electric storage water heater sub types", "$ref": "comCheck.schema.json#/definitions/SWHSystemSubTypeOptions", - "default": "UNKNOWN_SWH_SYSTEM_SUB_TYPE", - "effective_energy_codes": ["CEZ_90_1_2022", "CEZ_IECC2024"] + "effective_energy_codes": [ + "CEZ_90_1_2022", + "CEZ_IECC2024" + ] }, "listPosition": { - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, "efficiencyRequirementException": { "description": "High input natural gas service water heater efficiency requirement exceptions", "$ref": "comCheck.schema.json#/definitions/EquipmentEfficiencyRequirementExceptionOptions", - "default": "EFF_EXCEPTION_UNSPECIFIED", - "effective_energy_codes": ["CEZ_90_1_2022", "CEZ_IECC2024"] + "effective_energy_codes": [ + "CEZ_90_1_2022", + "CEZ_IECC2024" + ] }, "requirementAnswer": { - "type": "array" + "type": "array", + "items": { + "$ref": "comCheck.schema.json#/definitions/Requirements" + }, + "default": [] } }, "required": [], @@ -3613,37 +4720,54 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "bldgUseKey": { "description": "Building use Key, reference BuildingAreaUse key", - "type": "string" + "type": [ + "string", + "null" + ] }, "airInfiltration": { "description": "Air infiltration measured @75Pa", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "cfm/sqft", "$comment": "Used for energy credits: EFF_PACKAGE_REDUCED_AIR_INFILTRATION, EFF_PACKAGE_ENERGY_CREDIT_REDUCED_AIR_INFILTRATION, EFF_PACKAGE_ENERGY_CREDIT_MAS_REDUCED_AIR_INFILTRATION, EFF_PACKAGE_ENERGY_CREDIT_DENVER_REDUCED_AIR_INFILTRATION, EFF_PACKAGE_ENERGY_CREDIT_DENVER_FURTHER_REDUCED_AIR_INFILTRATION, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_REDUCE_AIR_INFILTRATION" }, "kitchenSize": { "description": "Kitchen size", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "sqft", "$comment": "Used for energy credits: EFF_PACKAGE_ENERGY_CREDIT_EFFICIENT_KITCHEN_EQUIPMENT, EFF_PACKAGE_ENERGY_CREDIT_MAS_EFFICIENT_KITCHEN_EQUIPMENT, EFF_PACKAGE_ENERGY_CREDIT_DENVER_EFFICIENT_KITCHEN_EQUIPMENT, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_EFFICIENT_COM_KITCHEN_EQUIPMENT" }, "renewableCapacity": { "description": "On-site renewable capacity, unit is Btu for thermal system and watts for electric system.", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_ONSITE_RENEWABLES, EFF_PACKAGE_ENERGY_CREDIT_DENVER_ONSITE_RENEWABLES, EFF_PACKAGE_ENERGY_CREDIT_DENVER_ENHANCED_RENEWABLES, EFF_PACKAGE_ENERGY_CREDIT_MAS_ONSITE_RENEWABLES]" }, "renewableType": { "description": "Renewable system type", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/EnergyCreditRenewableTypeOptions" } @@ -3652,7 +4776,10 @@ }, "fractionGrossFloorAreaServedByGSHP": { "description": "Fraction of gross floor area served by GSHP, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 1.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_GSHP_SYSTEM]" @@ -3660,7 +4787,9 @@ "gshpFieldSourceCapacityType": { "description": "GSHP field source capacity type, Added by 90.1 2022", "anyOf": [ - { "type": "null" }, + { + "type": "null" + }, { "$ref": "comCheck.schema.json#/definitions/GSHPFieldSourceCapacityTypeOptions" } @@ -3669,95 +4798,137 @@ }, "fractionGrossFloorAreaServedByCAV": { "description": "Fraction of gross floor area served by CAV, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 1.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_DEDICATED_OUTDOOR_AIR_SYSTEM, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_DEDICATED_OUTDOOR_AIR_SYSTEM]" }, "coolingEnergyRecoveryRatio": { "description": "Cooling energy recovery ratio, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 1.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_DEDICATED_OUTDOOR_AIR_SYSTEM, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_DEDICATED_OUTDOOR_AIR_SYSTEM]" }, "heatingEnergyRecoveryRatio": { "description": "Heating energy recovery ratio, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 1.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_DEDICATED_OUTDOOR_AIR_SYSTEM, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_DEDICATED_OUTDOOR_AIR_SYSTEM]" }, "percentageWaterPipingWithIncreasedInsulation": { "description": "Percentage water piping with increased insulation, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 100.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_PIPE_INSULATION_SHW, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_PIPE_INSULATION]" }, "numberShowersWithDrainHeatRecovery": { "description": "Number of showers with drain heat recovery, Added by 90.1 2022", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 1, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_HEAT_RECOVERY_SHOWER_SHW, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_HEAT_RECOVERY_SHOWER_SWH]" }, "totalNumberShowers": { "description": "Total number of showers, Added by 90.1 2022", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 1, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_HEAT_RECOVERY_SHOWER_SHW, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_HEAT_RECOVERY_SHOWER_SWH]" }, "fractionTunedAreaOfGrossLightedFloorArea": { "description": "Fraction of tuned area of gross lighted floor area, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 1.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_DIM_HIGH_END_TRIM_LIGHT]" }, "grossLightedFloorArea": { "description": "Gross lighted floor area, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "sqft", "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_DAYLIGHT_AREA_LIGHT, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_DAYLIGHT_AREA_LIGHT]" }, "actualDaylightAreaWithContinuousDim": { "description": "Actual daylight area with continous dimming control, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "sqft", "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_DAYLIGHT_AREA_LIGHT, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_DAYLIGHT_AREA_LIGHT]" }, "percentageLightingLoadManagement": { "description": "Percentage lighting load management, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "maximum": 1.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_LIGHT_LOAD_MANAGEMENT, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_LIGHT_LOAD_MGMT]" }, "installedElectricStorageCapacity": { "description": "Installed electric storage capacity, Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "Wh/sqft", "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_ELECTRIC_STORAGE_LOAD_MANAGEMENT, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_ELECTRIC_STORAGE]" }, "storageRatio": { "description": "HVAC cooling energy storage ratio, unit in ton-hours storage per ton of design-day cooling load , Added by 90.1 2022", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.5, "maximum": 4.0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_HVAC_COOL_STORAGE_LOAD_MANAGEMENT, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_COOLING_STORAGE]" }, "sumOfFloorsServedByClassAElevators": { "description": "Sum of floors served by each Class A elevators", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0, "$comment": "Used the type is in [EFF_PACKAGE_ENERGY_CREDIT_EFFICIENT_ELEVATOR, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_EFFICIENT_ELEVATOR]" }, "sumOfFloorsServedByClassBElevators": { "description": "sum of floors served by all building elevators and escalators", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "minimum": 0, "$comment": "Used when $.type is in [EFF_PACKAGE_ENERGY_CREDIT_EFFICIENT_ELEVATOR, EFF_PACKAGE_ENERGY_CREDIT_IECC2024_EFFICIENT_ELEVATOR]" }, @@ -3779,7 +4950,9 @@ ] } }, - "required": ["type"], + "required": [ + "type" + ], "additionalProperties": false }, "Renewable": { @@ -3787,7 +4960,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "renewableException": { @@ -3797,31 +4973,43 @@ "numberOfFloors": { "description": "number of floors", "type": "integer", - "minimum": 1 + "minimum": 0 }, "largestThreeFloorArea": { "description": "Gross floor area of the largest three floors", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, "requiredCapacity": { "description": "Code required minimum renewable capacity", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "This number is calculated by engine - user input is not valid and will always overriden by the engine" }, "proposedCapacity": { "description": "Sum of the proposed renewable capacity", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "watt", "$comment": "This number is calculated by engine based on the renewable systems defined in the project" }, "roofAreaForRenewable": { "description": "roof area for renewable systems", - "type": "number", + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "ft2" }, @@ -3834,20 +5022,24 @@ }, "requiredOffsiteRenewableEnergy": { "description": "Sum of the required off-site renewable energy procurement", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "kWh", "$comment": "This number is calculated by engine - user input is not valid and will always overriden by the engine" }, - "proposedOffsiteRenewableEnergy": { "description": "Sum of the proposed off-site renewable energy procurement", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "minimum": 0.0, "unit": "kWh", "$comment": "This number is calculated by engine based on the offsite renewable energy defined in the project" }, - "offsiteRenewableProcurement": { "description": "List of off-site renewable procurement", "type": "array", @@ -3872,12 +5064,18 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "description of the renewable system", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "renewableSystemType": { "description": "Renewable system type", @@ -3907,12 +5105,18 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "description": { "description": "description of the offsite renewable procurement", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "offsiteRenewableProcurementType": { "description": "Offsite renewable procurement type", @@ -3958,7 +5162,10 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "type": { @@ -3974,14 +5181,21 @@ "properties": { "id": { "description": "Scope-unique reference identifier for instances of this data group.", - "type": ["string", "integer"], + "type": [ + "string", + "integer" + ], "$comment": "All id data elements are shown as required so that every data group can be explcitly identified." }, "thermalBridgeType": { "description": "thermal bridge type", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ThermalBridgeTypeOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ThermalBridgeTypeOptions" + } ], "effective_energy_codes": [ "CEZ_90_1_2022", @@ -3992,8 +5206,12 @@ "thermalBridgeCategory": { "description": "thermal bridge category", "anyOf": [ - { "type": "null" }, - { "$ref": "comCheck.schema.json#/definitions/ThermalBridgeCategoryOptions" } + { + "type": "null" + }, + { + "$ref": "comCheck.schema.json#/definitions/ThermalBridgeCategoryOptions" + } ], "effective_energy_codes": [ "CEZ_90_1_2022", @@ -4007,20 +5225,32 @@ }, "psiFactor": { "description": "Psi factor", - "type": ["number", "null"] + "type": [ + "number", + "null" + ] }, "thermalBridgeLength": { "description": "linear length of a thermal bridge - ft", - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "$comment": "Use in conjunction with psiFactor" }, "chiFactor": { "description": "Chi factor", - "type": ["number", "null"] + "type": [ + "number", + "null" + ] }, "numberOfPoints": { "description": "Number of points", - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "$comment": "Use in conjunction with ChiFactor" } }, @@ -4028,9 +5258,20 @@ "additionalProperties": false }, "ProjectTypeOptions": { - "type": "string", - "enum": ["ADDITION", "ALTERATION", "NEW_CONSTRUCTION"], - "descriptions": ["Addition", "Alteration", "New construction"] + "enum": [ + "ADDITION", + "ALTERATION", + "NEW_CONSTRUCTION", + "NONE", + null + ], + "descriptions": [ + "Addition", + "Alteration", + "New construction", + "Unspecified", + "Missing" + ] }, "ProjectSubTypeOptions": { "type": "string", @@ -4066,35 +5307,66 @@ }, "BuildingUseTypeOptions": { "type": "string", - "enum": ["NONE", "WHOLE_BLDG", "ACTIVITY", "UNKNOWN_BLDG_USE"], - "descriptions": ["None", "Whole Bldg", "Activity", "Unknown"] + "enum": [ + "NONE", + "WHOLE_BLDG", + "ACTIVITY", + "UNKNOWN_BLDG_USE" + ], + "descriptions": [ + "None", + "Whole Bldg", + "Activity", + "Unknown" + ] }, "ConditionTypeOptions": { "type": "string", - "enum": ["COOLING", "HEATING", "HEATING_AND_COOLING", "NONE"], - "descriptions": ["Cooling", "Heating", "Heating and Cooling", "None"] + "enum": [ + "COOLING", + "HEATING", + "HEATING_AND_COOLING", + "NONE" + ], + "descriptions": [ + "Cooling", + "Heating", + "Heating and Cooling", + "None" + ] }, "EnergyCodeOptions": { "type": "string", "enum": [ + "CEZ_IECC2009", + "CEZ_IECC2012", "CEZ_IECC2015", "CEZ_IECC2018", "CEZ_IECC2021", "CEZ_IECC2024", + "CEZ_IECC2024_APPXCF", + "CEZ_90_1_2007", + "CEZ_90_1_2010", "CEZ_90_1_2013", "CEZ_90_1_2016", "CEZ_90_1_2019", - "CEZ_90_1_2022" + "CEZ_90_1_2022", + "NONE" ], "descriptions": [ + "IECC 2012", "IECC 2015", "IECC 2018", "IECC 2021", "IECC 2024", + "IECC 2024 Appendix CF", + "ASHRAE 90.1 2007", + "ASHRAE 90.1 2010", "ASHRAE 90.1 2013", "ASHRAE 90.1 2016", "ASHRAE 90.1 2019", - "ASHRAE 90.1 2022" + "ASHRAE 90.1 2022", + "Unspecified" ] }, "StateRegionEnergyCodeOptions": { @@ -4103,6 +5375,10 @@ "CEZ_ONTARIO", "CEZ_DC2017", "CEZ_PUERTO_RICO", + "CEZ_NYS2024_IECC2024", + "CEZ_NYS2025_9012022", + "CEZ_NYC2025_IECC2024", + "CEZ_NYC2025_9012022", "CEZ_NYSTRETCH_90_1_2016", "CEZ_NYSTRETCH_NYC_90_1_2016", "CEZ_NYSTRETCH_90_1_11_G", @@ -4110,6 +5386,7 @@ "CEZ_CHICAGO_IECC2022", "CEZ_FL", "CEZ_VT", + "CEZ_VT2024_IECC2021", "CEZ_NY", "CEZ_NEWYORKCITY", "CEZ_NYSTRETCH_NYC_IECC2018", @@ -4117,15 +5394,21 @@ "CEZ_CO_BOULDER", "CEZ_CO_DENVER", "CEZ_CO_DENVER_90_1_2016", + "CEZ_LA2021_IECC2021", "CEZ_MN", "CEZ_MAS", "CEZ_MAS_STRETCH_IECC2021", - "CEZ_CO_DENVER_IECC2021" + "CEZ_CO_DENVER_IECC2021", + "NONE" ], "descriptions": [ "2012 Ontario Building Code and Chapter 2 of Division 3 of SB-10 (2017)", "2017 DC Energy Code", "Puerto Rico Commercial Energy Code", + "2024 New York State Energy Conservation Code - IECC 2024", + "2025 New York State Energy Conservation Code - 90.1 (2022)", + "2025 New York City Energy Conservation Code - 90.1 (2022)", + "2025 New York City Energy Conservation Code - IECC 2024", "2020 NYStretch Energy Code - 90.1 (2016) Standard", "2020 New York City Energy Conservation Code, Appendix CA (modified 90.1-2016)", "2020 NYStretch Energy Code - 90.1 (2016) Sec 11/App G, Modeling Envelope Backstop", @@ -4133,6 +5416,7 @@ "2022 Chicago Energy Transformation Code", "2020 Florida - 7th Edition", "2020 Vermont Commercial Building Energy Standards", + "2024 Vermont Commercial Building Energy Standards", "2020 New York Energy Conservation Construction Code", "2016 New York City Energy Conservation Code", "2020 New York City Energy Conservation Code", @@ -4140,26 +5424,38 @@ "2020 City of Boulder, Colorado Energy Conservation Code", "2019 Denver, Colorado Energy Conservation Code - 2018 IECC", "2019 Denver, Colorado Energy Conservation Code - 90.1 (2016)", + "2021 LA Energy Code - 2021 IECC", "2024 Minnesota Commercial Energy Code", "2020 Massachusetts Base Energy Code", "2023 Massachusetts Stretch Energy Code", - "2022 Denver Energy Code" + "2022 Denver Energy Code", + "Unspecified" ] }, "ComplianceModeOptions": { "type": "string", - "enum": ["UA", "PERFORMANCE", "PRESCRIPTIVE"], - "descriptions": ["UA", "Performance", "Prescriptive"] + "enum": [ + "UA", + "PERFORMANCE", + "PRESCRIPTIVE" + ], + "descriptions": [ + "UA", + "Performance", + "Prescriptive" + ] }, "AirBarrierComplianceTypeOptions": { "type": "string", "enum": [ + "AIR_BARRIER_OPTION_CONTINUITY_PLAN", "AIR_BARRIER_OPTION_UNKNOWN", "AIR_BARRIER_OPTION_MATERIAL_PERM", "AIR_BARRIER_OPTION_ASSEMBLY_PERM", "AIR_BARRIER_OPTION_LEAKAGE_TEST" ], "descriptions": [ + "Continuity Plan", "Unspecified", "Air Barrier Permeability", "Assembly Permeability", @@ -4177,20 +5473,27 @@ "METAL_BLDG_AG_WALL", "CONCRETE_AG_WALL", "MASONRY_AG_WALL", - "OTHER_AG_WALL" + "OTHER_AG_WALL", + "OTHER_BG_WALL", + "OTHER_FRAME", + null ], "descriptions": [ "Wood-Framed, 16in. o.c.", "Wood-Framed, 24in. o.c.", "Steel-Framed, 16in. o.c.", "Steel-Framed, 24in. o.c.", + "Metal Building Wall Without Thermal Break", "Metal Building Wall", "Solid Concrete Wall", - "Concrete Block" + "Concrete Block", + "Other Above Grade Wall Type", + "Other Below Grade Wall Type", + "Other Framing Type", + "Unspecified" ] }, "BgWallTypeOptions": { - "type": "string", "enum": [ "SOLID_CONCRETE_LE_8IN_BG_WALL", "SOLID_CONCRETE_GT_8IN_BG_WALL", @@ -4201,7 +5504,8 @@ "CONCRETE_BG_WALL", "WOOD_BG_WALL", "MASONRY_BG_WALL", - "OTHER_BG_WALL" + "OTHER_BG_WALL", + null ], "descriptions": [ "Solid concrete or masonry below grade wall <= 8 inches", @@ -4213,7 +5517,8 @@ "Concrete below grade wall", "Wood below grade wall", "mansory unit below grade wall", - "Other below grade wall type" + "Other below grade wall type", + "Unspecified" ] }, "RoofTypeOptions": { @@ -4222,6 +5527,7 @@ "ABOVE_DECK_ROOF", "METAL_BLDG_STANDING_SEAM_ROOF", "METAL_BLDG_SCREW_DOWN_ROOF", + "METAL_ROOF_W_THERMAL_BREAK", "WOOD_STD_JOIST_TRUSS", "NON_WOOD_JOIST_TRUSS", "OTHER_ROOF" @@ -4230,6 +5536,7 @@ "Insulation Entirely Above Deck", "Metal Building, Standing Seam", "Metal Building, Screw Down", + "Metal Roof with Thermal Break", "Attic Roof, Wood Joists", "Attic Roof, Steel Joists", "Other (U-Factor option)" @@ -4264,6 +5571,7 @@ "type": "string", "enum": [ "HA_ROOF_REQ_UNSPECIFIED", + "HA_ROOF_REQ_SOLAR_REFLECTANCE", "HA_ROOF_REQ_SOLAR_REFLECTANCE_INDEX", "HA_ROOF_REQ_SOLAR_REFLECTANCE_INDEX_THERMAL_EMITTANCE", "HA_ROOF_EXEMPTION_VENTILATED_ATTIC", @@ -4280,6 +5588,7 @@ ], "descriptions": [ "Unspecified", + "Minimum Solar Reflectance", "3-yr Aged Solar Reflectance Index", "3-yr Aged Solar Reflectance Index and Thermal Transmittance", "Increased Insulation", @@ -4314,14 +5623,14 @@ ] }, "FloorTypeOptions": { - "type": "string", "enum": [ "ALL_WOOD_JOIST_TRUSS_FLOOR", "NON_WOOD_JOIST_TRUSS_FLOOR", "STRUCTURAL_SLAB_FLOOR", "HEATED_SLAB_ON_GRADE", "UNHEATED_SLAB_ON_GRADE", - "OTHER_FLOOR" + "OTHER_FLOOR", + null ], "descriptions": [ "Wood-Framed", @@ -4329,7 +5638,8 @@ "Concrete Floor (over unconditioned space)", "Slab-On-Grade (Heated)", "Slab-On-Grade (Unheated)", - "Other (U-Factor option)" + "Other (U-Factor option)", + "Unspecified" ] }, "FloorExposedFrameType": { @@ -4349,18 +5659,36 @@ }, "SlabInsulationPositionOptions": { "type": "string", - "enum": ["HORIZONTAL", "VERTICAL", "NO_INSULATION"], - "descriptions": ["Horizontal with vertical Slab Insulation", "Vertical", "None"] + "enum": [ + "HORIZONTAL", + "VERTICAL", + "NO_INSULATION", + "NONE" + ], + "descriptions": [ + "Horizontal with vertical Slab Insulation", + "Vertical", + "None", + "None" + ] }, "AgWallConstructionDetailsTypeOptions": { "type": "string", "enum": [ + "AG_WALL_CONSTRUCTION_DETAILS_UNKNOWN", + "AG_WALL_CONSTRUCTION_DETAILS_HORIZONTAL_Z_GIRTS", + "AG_WALL_CONSTRUCTION_DETAILS_VERTICAL_Z_GIRTS", + "AG_WALL_CONSTRUCTION_DETAILS_Z_GIRTS_THERMAL_BROKEN", "AG_WALL_METAL_BLDG_SINGLE_LAYER_MINERAL_FIBER", "AG_WALL_METAL_BLDG_DOUBLE_LAYER_MINERAL_FIBER", "AG_WALL_METAL_BLDG_SINGLE_LAYER_MINERAL_FIBER_IN_CAVITY", "NONE" ], "descriptions": [ + "Unknown", + "Horizontal Z-Girts", + "Vertical Z-Girts", + "Z-Girts with Thermal Break", "Single Layer Mineral Fiber (comopressed at girt)", "Single Layer Mineral Fiber (in cavity, thermal block at girt)", "Double Layer Mineral Fiber (outer layer compressed at girt)", @@ -4402,60 +5730,114 @@ ] }, "EnvelopeAssemblyAllowanceTypeOptions": { - "type": "string", "enum": [ "ENV_ALLOWANCE_NONE", "ENV_ALLOWANCE_FEN_SIX_FEET_ABOVE_FLOOR", "ENV_ALLOWANCE_SKYLIGHT_HAS_DAYLIGHT_AND_AUTOCONTROLS", - "ENV_ALLOWANCE_DYNAMIC_GLAZING" + "ENV_ALLOWANCE_DYNAMIC_GLAZING", + "NONE", + null ], "descriptions": [ "None", "Fenestration six foot above floor", "Skylight has daylight and auto controls", - "Dyanmic glazing" + "Dyanmic glazing", + "None", + "Unspecified" ] }, "CMUTypeOptions": { - "type": "string", "enum": [ "CMU_SOLID_GROUT", "CMU_PARTIAL_GROUT_CELLS_EMPTY", "CMU_PARTIAL_GROUT_CELLS_INSULATED", "CMU_UNREINFORCED_CELLS_EMPTY", - "CMU_UNREINFORCED_CELLS_INSULATED" + "CMU_UNREINFORCED_CELLS_INSULATED", + "NONE", + null ], "descriptions": [ "Solid Grouted", "Partially Grouted, Cells Empty", "Partially Grouted, Cells Insulated", "Unreinforced, Cells Empty", - "Unrefinforced, Cells Insulated" + "Unreinforced, Cells Insulated", + "None", + "Unspecified" ] }, "ConcreteDensityOptions": { - "type": "integer", - "enum": [0, 95, 115, 144], - "descriptions": ["Not available", "Light Weight", "Medium Weight", "Normal Weight"] + "enum": [ + 0, + 85, + 95, + 115, + 135, + 144, + null + ], + "descriptions": [ + "Not available", + "Light Weight", + "Medium Weight", + "Normal Weight", + "Unspecified" + ] }, "ConcreteThicknessOptions": { - "type": "integer", - "enum": [0, 6, 8, 10, 12] + "enum": [ + 0, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + null + ] }, "ConstructionTypeOptions": { "type": "string", - "enum": ["NON_RESIDENTIAL", "RESIDENTIAL", "SEMI_HEATED"], - "descriptions": ["Non residential", "Residential", "Semi-heated"] + "enum": [ + "NON_RESIDENTIAL", + "RESIDENTIAL", + "SEMI_HEATED" + ], + "descriptions": [ + "Non residential", + "Residential", + "Semi-heated" + ] }, "EnvelopeAssemblyExemptionOptions": { "type": "string", - "enum": ["ENV_EXEMPTION_NONE", "ENV_EXEMPTION_DYNAMIC_GLAZING"], - "descriptions": ["None", "Dynamic glazing"] + "enum": [ + "ENV_EXEMPTION_NONE", + "ENV_EXEMPTION_DYNAMIC_GLAZING", + "NONE" + ], + "descriptions": [ + "None", + "Dynamic glazing", + "None" + ] }, "FurringTypeOptions": { "type": "string", - "enum": ["WOOD_FURRING", "METAL_FURRING", "NO_FURRING", "OTHER_FURRING"], + "enum": [ + "NONE", + "WOOD_FURRING", + "METAL_FURRING", + "NO_FURRING", + "OTHER_FURRING" + ], "descriptions": [ + "None", "Furring: Wood", "Furring: Metal", "Furring: None", @@ -4463,7 +5845,6 @@ ] }, "OrientationOptions": { - "type": "string", "enum": [ "NORTH", "EAST", @@ -4473,7 +5854,8 @@ "NORTHEAST", "SOUTHWEST", "SOUTHEAST", - "UNSPECIFIED_ORIENTATION" + "UNSPECIFIED_ORIENTATION", + null ], "descriptions": [ "North", @@ -4484,7 +5866,8 @@ "North east", "South west", "South east", - "Unspecified orientation" + "Unspecified orientation", + "Null" ], "comments": [ "Applicable to IECC and 90.1", @@ -4495,13 +5878,22 @@ "Applicable to 90.1 only", "Applicable to 90.1 only", "Applicable to 90.1 only", - "Applicable to IECC and 90.1" + "Applicable to IECC and 90.1", + "Null" ] }, "InsulationPositionOptions": { "type": "string", - "enum": ["HORIZONTAL", "VERTICAL", "NO_INSULATION"], - "descriptions": ["Horizontal", "Vertical", "None"] + "enum": [ + "HORIZONTAL", + "VERTICAL", + "NO_INSULATION" + ], + "descriptions": [ + "Horizontal", + "Vertical", + "None" + ] }, "AltExemptTypeOptions": { "type": "string", @@ -4518,6 +5910,8 @@ "EXEMPT_GLAZING_STORM_ONLY", "EXEMPT_GLAZING_GLAZING_REPLACEMENT", "EXEMPT_GLAZING_UNIT_REPLACEMENT_LT_25_PCT", + "EXEMPT_HISTORIC_CHARACTERISTIC", + "EXEMPT_LIGHTING_SPACE_REPLACEMENT_LT_20_PCT_LOAD", "EXEMPT_LIGHTING_SPACE_REPLACEMENT_LT_50_PCT", "EXEMPT_MECH_REPAIR_ONLY", "EXEMPT_MECH_EXTENSIVE_OTHER_MODS", @@ -4552,6 +5946,8 @@ "Storm window alteration.", "Glazing replacement in existing sash or frame.", "Less than 25% fenestration area alteration.", + "Alteration to the area is not applicable to historic characteristics.", + "Less than 20% fixture replacement.", "Less than 50% fixture replacement.", "Modification or repair without energy use change.", "Extensive ancillary requirements.", @@ -4575,7 +5971,6 @@ ] }, "FenestrationFrameTypeOptions": { - "type": "string", "enum": [ "METAL", "METAL_W_THERMAL_BREAK", @@ -4583,7 +5978,20 @@ "VINYL", "CURTAIN_WALL", "CURTAIN_WALL_STOREFRONT", - "OTHER_FRAME" + "NON_METAL", + "NONE", + "OTHER_FRAME", + "METAL_FRAME_24_AG_WALL", + "GLASS_DOOR", + "METAL_THERMAL_BREAK", + "OTHER_DOOR", + "INSUL_METAL_DOOR", + "NO_INSUL_SINGLE_METAL_DOOR", + "WOOD_FRAME_16_AG_WALL", + "ALL_WOOD_JOIST_TRUSS_FLOOR", + "METAL_FRAME_16_AG_WALL", + "WOOD_DOOR", + null ], "descriptions": [ "Metal frame", @@ -4591,7 +5999,21 @@ "Wood frame", "Vinyl frame", "Curtain wall", - "Curtain wall for store front" + "Curtain wall (storefront)", + "Non-metal frame", + "None", + "Other frame", + "24-gauge metal-framed wall", + "Glass door", + "Metal frame with thermal break", + "Other door", + "Insulated metal door", + "Non-insulated single metal door", + "16-gauge wood-framed wall", + "All-wood joist/truss floor", + "16-gauge metal-framed wall", + "Wood door", + "Unspecified" ] }, "GlazingTypeOptions": { @@ -4600,45 +6022,86 @@ "SINGLE_PANE", "DOUBLE_PANE", "DOUBLE_PANE_LOWE", + "OTHER_GLAZING", "TRIPLE_PANE", - "TRIPLE_PANE_LOWE" + "TRIPLE_PANE_LOWE", + "NONE" ], "descriptions": [ "Single pane", "Double pane", "Double pane low-e", "Triple pane", - "Tripe pane low-e" + "Tripe pane low-e", + "None" ] }, "SolarTypeOptions": { "type": "string", - "enum": ["CLEAR", "TINTED", "REFLECTIVE", "OTHER_SOLAR"], - "descriptions": ["Clear", "Tinted", "Reflective", "Other solar coating type"] + "enum": [ + "CLEAR", + "TINTED", + "REFLECTIVE", + "OTHER_SOLAR", + "NONE" + ], + "descriptions": [ + "Clear", + "Tinted", + "Reflective", + "Other solar coating type", + "None" + ] }, "WindowProductionTypeOptions": { "type": "string", - "enum": ["FACTORY_ASSEMBLED_WINDOW", "SITE_BUILT_WINDOW"], - "descriptions": ["Factory Assembled", "Site-Built"] + "enum": [ + "FACTORY_ASSEMBLED_WINDOW", + "SITE_BUILT_WINDOW" + ], + "descriptions": [ + "Factory Assembled", + "Site-Built" + ] }, "PerfDataTypeOptions": { "type": "string", - "enum": ["PERF_TYPE_NFRC", "PERF_TYPE_OTHER", "PERF_TYPE_DEFAULT"], + "enum": [ + "NONE", + "PERF_TYPE_NFRC", + "PERF_TYPE_OTHER", + "PERF_TYPE_DEFAULT", + "PERF_TYPE_UNSPECIFIED" + ], "descriptions": [ + "None", "NFRC site-built certified product", "Product performance evaluated in accordance with NFRC", - "Energy code defaults" + "Energy code defaults", + "Unspecified" ] }, "WindowOpenTypeOptions": { "type": "string", - "enum": ["NON_OPERABLE_WINDOW", "OPERABLE_WINDOW"], - "descriptions": ["Not operable", "Operable"] + "enum": [ + "NON_OPERABLE_WINDOW", + "OPERABLE_WINDOW" + ], + "descriptions": [ + "Not operable", + "Operable" + ] }, "SkylightCurbTypeOptions": { "type": "string", - "enum": ["CURB_SKYLIGHT", "NO_CURB_SKYLIGHT"], - "descriptions": ["Has curb", "No curb"] + "enum": [ + "CURB_SKYLIGHT", + "NO_CURB_SKYLIGHT" + ], + "descriptions": [ + "Has curb", + "No curb" + ] }, "AdjacentSpaceTypeOptions": { "type": "string", @@ -4659,8 +6122,16 @@ }, "GlazingMaterialTypeOptions": { "type": "string", - "enum": ["GLASS_GLAZING_MAT", "PLASTIC_GLAZING_MAT"], - "descriptions": ["Glazing", "Plastic"] + "enum": [ + "GLASS_GLAZING_MAT", + "PLASTIC_GLAZING_MAT", + "NONE" + ], + "descriptions": [ + "Glazing", + "Plastic", + "None" + ] }, "DoorTypeOptions": { "type": "string", @@ -4670,6 +6141,7 @@ "INSUL_METAL_DOOR", "WOOD_DOOR", "GLASS_DOOR", + "METAL_W_THERMAL_BREAK", "OTHER_DOOR", "UPWARD_ACTING_SECTIONAL" ], @@ -4679,43 +6151,80 @@ "Insulated Metal", "Wood", "Glass (over 50% glazing)", + "Metal with Thermal Break", "Other (U-Factor option)", "Upward acting section, VT 2020" ] }, "DoorOpenTypeOptions": { "type": "string", - "enum": ["SWINGING_DOOR", "NON_SWINGING_DOOR", "GARAGE_DOOR"], - "descriptions": ["Swinging door", "Non-swinging door", "Garage door"] + "enum": [ + "SWINGING_DOOR", + "NON_SWINGING_DOOR", + "GARAGE_DOOR" + ], + "descriptions": [ + "Swinging door", + "Non-swinging door", + "Garage door" + ] }, "DoorEntranceTypeOptions": { "type": "string", - "enum": ["ENTRANCE_DOOR", "NON_ENTRANCE_DOOR", "OTHER_ENTRANCE"], - "descriptions": ["Entrance door", "Non-Entrance door", "Other"] + "enum": [ + "ENTRANCE_DOOR", + "NON_ENTRANCE_DOOR", + "OTHER_ENTRANCE" + ], + "descriptions": [ + "Entrance door", + "Non-Entrance door", + "Other" + ] }, "DoorGlazingFrameTypeOptions": { "type": "string", - "enum": ["METAL", "NON_METAL"], - "descriptions": ["Metal", "Non-metal"] + "enum": [ + "METAL", + "NON_METAL" + ], + "descriptions": [ + "Metal", + "Non-metal" + ] }, "LightingAllowanceTypeOptions": { - "type": "string", "enum": [ "ALLOWANCE_NONE", + "ALLOWANCE_ADVANCED_CONTROLS", "ALLOWANCE_DECORATIVE_APPEARANCE", + "ALLOWANCE_DECORATIVE_APPEARANCE_LOBBIES", + "ALLOWANCE_DECORATIVE_APPEARANCE_OTHER", "ALLOWANCE_DISPLAY_TERMINAL", + "ALLOWANCE_ELECTRICAL_MECHANICAL", "ALLOWANCE_VEHICLE_SPORT_ELECT_HIGHLIGHT", "ALLOWANCE_FURNITURE_CLOTHES_COSMETIC_HIGHLIGHT", "ALLOWANCE_JEWELRY_CRYSTAL_CHINA_HIGHLIGHT", - "ALLOWANCE_OTHER_HIGHLIGHT" + "ALLOWANCE_OTHER_HIGHLIGHT", + "ALLOWANCE_VIDEO_CONFERENCE", + "NONE", + null ], "descriptions": [ "None", + "Advanced Controls", "Decorative Appearance", + "Decorative Appearance, Lobbies", + "Decorative Appearance, Other", + "Display Terminal", + "Electrical/Mechanical Equipment", "Vehicles, sporting goods, small electronics, highlighting", "Furniture, clothing, cosmetics highlighting", "Jewelry, crystal, china highlighting", - "Other retail highlighting" + "Other retail highlighting", + "Video conference", + "None", + "Unspecified" ] }, "LightingExemptionTypeOptions": { @@ -4724,30 +6233,50 @@ "EXEMPTION_NONE", "EXEMPTION_ADVERTISING_OR_DIRECTION_SIGN", "EXEMPTION_ATHLETIC_PLAY_AREA", + "EXEMPTION_APPROVED_SAFETY", "EXEMPTION_CASINO_GAMING", "EXEMPTION_DRESSING_ROOM_MIRROR", + "EXEMPTION_DWELL_UNIT_CONTROLLED", "EXEMPTION_EDUCATION", + "EXEMPTION_EMERGENCY_AUTOOFF", "EXEMPTION_EMERGENCY_LIGHT", "EXEMPTION_EQUIPMENT", "EXEMPTION_EXIT_SIGN", "EXEMPTION_FOOD_PREPERATION", "EXEMPTION_FURNITURE_SUPPLEMENTAL", + "EXEMPTION_HIGHLIGHT_HAZARDS", "EXEMPTION_HIGHLIGHT_LANDMARK", + "EXEMPTION_INDUSTRIAL_PRODUCTION", + "EXEMPTION_MANUFACTURER_AS_PART_OF_EQUIP", "EXEMPTION_MEDICAL_PROCEDURE", "EXEMPTION_PARKING_GARAGE_TRANSITION", "EXEMPTION_PLANT", + "EXEMPTION_POOLS_WATER", "EXEMPTION_REFRIGERATOR", "EXEMPTION_RELIGIOUS_PULPIT_CHOIR", + "EXEMPTION_REQUIRED_EGRESS", "EXEMPTION_RETAIL_DISPLAY", + "EXEMPTION_TEMP_LIGHTING", "EXEMPTION_THEATER_APPLICATION", - "EXEMPTION_HEALTH_SAFETY_REG" + "EXEMPTION_THEME_PARK_ELEMENTS", + "EXEMPTION_HEALTH_SAFETY_REG", + "EXEMPTION_HIGHLIGHT_MONUMENT", + "EXEMPTION_TRANSPORTATION_MARKER", + "EXEMPTION_TRANSPORTATION_SITE", + "EXEMPTION_EMERGENCY_LIGHT_OFF_NORMAL_BUSINESS_HRS", + "EXEMPTION_MUSEUM_DISPLAY", + "EXEMPTION_SEARCHLIGHTS", + "EXEMPTION_SLEEPING_UNIT", + "EXEMPTION_VISUALLY_IMPAIRED" ], "descriptions": [ "None", "Advertising/Directional Signage", "Athletic TV Broadcasting", + "Approved Safety Lighting", "Casino Gaming", "Dressing Room Mirror Lighting", + "Dwelling Unit Lighting Controlled by Occupant", "Lighting Sales or Educational Demonstration Systems", "Emergency Lighting Auto-off During Operating Hours", "Lighting Integral to Equipment", @@ -4755,14 +6284,25 @@ "Food Preparation Equipment", "Furniture-mounted Supplemental Task Lighting", "Registered Historical Landmark", + "Industrial Production", "Medical/Dental Procedure Lighting", "Parking Garage Transition Lighting", "Non-Human Life Support Lighting", "Lighting in Refrigerator/Freezer Cases", "Religious Pulpit and Choir Lighting", + "Lighting Required for Egress", + "Temporary Lighting", "Retail Display Window", "Theatrical Lighting", - "Health, Safety Required by Regulation" + "Theme Park Elements", + "Health, Safety Required by Regulation", + "Highlight Monument", + "Transportation Marker", + "Transporation Site Lighting", + "Emergency Lighting Auto-off During Operating Hours", + "Museum Display", + "Searchlights", + "Visually Impaired" ] }, "LightingTypeOptions": { @@ -4872,22 +6412,29 @@ "TrackLightingWattageBasisTypeOptions": { "type": "string", "enum": [ + "NONE", "WATTAGE_BASIS_NOT_SET", "TRACK_BASIS", "CIRCUIT_BREAKER_CAPACITY", "CURRENT_LIMITING_DEVICE_CAPACITY", - "TRANSFORMER_CAPACITY" + "TRANSFORMER_CAPACITY", + null ], "descriptions": [ + "None", "Unknown", "Line-voltage track luminaire wattage", "Line-voltage circuit breaker capacity", "Line-voltage current-limiter device capacity", - "Low-voltage transformer capacity" + "Low-voltage transformer capacity", + "Unspecified" ] }, "AdvancedControlsAllowanceTypeOptions": { - "type": "string", + "type": [ + "string", + "null" + ], "enum": [ "ALLOWANCE_ADV_CONTROLS_NOT_SPECIFIED", "ALLOWANCE_ADV_CONTROLS_DIM_MANUAL_CONT", @@ -4896,7 +6443,8 @@ "ALLOWANCE_ADV_CONTROLS_MULT_LEVEL_OCC_SENSORS", "ALLOWANCE_ADV_CONTROLS_OCC_SENSORS_WORKSTATION_CONT_DIM", "ALLOWANCE_ADV_CONTROLS_OCC_SENSORS_WORKSTATION_CONT_DIM_PLUS_MANUAL_DIM", - "ALLOWANCE_ADV_CONTROLS_AUTO_BILEVEL" + "ALLOWANCE_ADV_CONTROLS_AUTO_BILEVEL", + null ], "descriptions": [ "Unspecified", @@ -4981,6 +6529,7 @@ "WHOLE_BUILDING_HEALTH_CLINIC", "WHOLE_BUILDING_HOSPITAL", "WHOLE_BUILDING_HOTEL", + "WHOLE_BUILDING_INVALID_USE", "WHOLE_BUILDING_LIBRARY", "WHOLE_BUILDING_MANUFACTURING", "WHOLE_BUILDING_MOTEL", @@ -5021,6 +6570,7 @@ "Health clinic", "Hospital", "Hotel", + "Invalid Use", "Library", "Manufacturing", "Motel", @@ -5059,7 +6609,8 @@ "EXT_ZONE_PARKS", "EXT_ZONE_FOREST", "EXT_ZONE_RURAL", - "EXT_ZONE_OTHER" + "EXT_ZONE_OTHER", + "EXT_ZONE_UNDEVELOPED" ], "descriptions": [ "Unspecified", @@ -5071,7 +6622,8 @@ "Developed area in national or state park (LZ1)", "Developed area on forest land (LZ2)", "Developed rural area (LZ1)", - "Other (LZ3)" + "Other (LZ3)", + "Undeveloped area (LZ1)" ] }, "ThermalBridgeTypeOptions": { @@ -5141,13 +6693,18 @@ ] }, "ThermalBridgeComplianceTypeOptions": { - "type": "string", "enum": [ "THERMAL_BRIDGE_NON_PRESCRIPTIVE", "THERMAL_BRIDGE_PRESCRIPTIVE", - " THERMAL_BRIDGE_AS_DESIGNED" + "THERMAL_BRIDGE_AS_DESIGNED", + null ], - "descriptions": ["Non-Prescriptive", "Prescriptive", "As-Designed"] + "descriptions": [ + "Non-Prescriptive", + "Prescriptive", + "As-Designed", + "Unspecified" + ] }, "ThermalBridgeExceptionTypeOptions": { "type": "string", @@ -5186,7 +6743,6 @@ ] }, "CondenserTypeOptions": { - "type": "string", "enum": [ "UNKNOWN_CONDENSER", "NO_CONDENSER", @@ -5197,7 +6753,8 @@ "GLYCOL_COOLED", "AIR_COOLED_FAD_CONDENSER", "AIR_COOLED_DUCTED_CONDENSER", - "CHILLED_WATER" + "CHILLED_WATER", + null ], "descriptions": [ "Unknown", @@ -5209,7 +6766,8 @@ "Glycol Cooled", "Air Cooled Free Discharge", "Air Cooled Ducted", - "Chilled Water" + "Chilled Water", + "Unspecified" ] }, "CoolingEquipmentTypeOptions": { @@ -5295,8 +6853,20 @@ }, "EconomizerTypeOptions": { "type": "string", - "enum": ["UNKNOWN_ECONOMIZER", "NO_ECONOMIZER", "AIR_ECONOMIZER", "WATER_ECONOMIZER"], - "descriptions": ["Unknown", "None", "Air", "Water"] + "enum": [ + "UNKNOWN_ECONOMIZER", + "NO_ECONOMIZER", + "AIR_ECONOMIZER", + "WATER_ECONOMIZER", + "FLUID_ECONOMIZER" + ], + "descriptions": [ + "Unknown", + "None", + "Air", + "Water", + "Fluid" + ] }, "EconomizerExceptionOptions": { "type": "string", @@ -5353,26 +6923,29 @@ ] }, "FuelTypeOptions": { - "type": "string", "enum": [ "UNKNOWN_FUEL", "GAS", "ELECTRIC", "OIL", + "OIL_RESIDUAL", "PROPANE", "HOTWATER", "STEAM", - "OTHER_FOSSIL_FUEL" + "OTHER_FOSSIL_FUEL", + null ], "descriptions": [ "Unknown", "Natural Gas", "Electric", "Oil", + "Residual Oil", "Propane", "Hot Water", "Steam", - "Other Fossil Fuel" + "Other Fossil Fuel", + "Unspecified" ] }, "SpaceHeatingSystemExceptionOptions": { @@ -5424,8 +6997,16 @@ }, "ZoneLayoutOptions": { "type": "string", - "enum": ["UNKNOWN_ZONE", "SINGLE_ZONE", "MULTI_ZONE"], - "descriptions": ["Unknwon", "Single zone", "Multi zone"] + "enum": [ + "UNKNOWN_ZONE", + "SINGLE_ZONE", + "MULTI_ZONE" + ], + "descriptions": [ + "Unknwon", + "Single zone", + "Multi zone" + ] }, "HeatPumpTypeOptions": { "type": "string", @@ -5495,20 +7076,28 @@ ] }, "BoilerDraftTypeOptions": { - "type": "string", - "enum": ["NATURAL_DRAFT", "FORCED_DRAFT"], - "descriptions": ["manual draft", "froce draft"] + "enum": [ + "NATURAL_DRAFT", + "FORCED_DRAFT", + null + ], + "descriptions": [ + "manual draft", + "froce draft", + "Unspecified" + ] }, "ChillerTypeOptions": { - "type": "string", "enum": [ "NO_CHILLER", "CENTRIFUGAL", + "CENTRIFUGAL_NON_STANDARD", "ROTARY_SCREW_OR_SCROLL", "RECIPROCATING", "SINGLE_EFFECT_ABSORPTION", "DOUBLE_EFFECT_ABSORPTION_DIRECT_FIRED", - "DOUBLE_EFFECT_ABSORPTION_INDIRECT_FIRED" + "DOUBLE_EFFECT_ABSORPTION_INDIRECT_FIRED", + null ], "descriptions": [ "None", @@ -5518,76 +7107,97 @@ "Reciprocating", "Single Effect Absorption", "Double Effect Absorption Indirect Fired", - "Double Effect Absorption Direct Fired" + "Double Effect Absorption Direct Fired", + "Unspecified" ] }, "CoolingPlantTypeOptions": { - "type": "string", "enum": [ "UNKNOWN_PLANT_COOLING", "NO_PLANT_COOLING", "CONDENSER_UNIT", "WATER_CHILLER", - "HEATPUMP_CHILLER" + "HEATPUMP_CHILLER", + null ], "descriptions": [ "Unknown", "None", "Condenser Unit", "Water Chiller", - "Heat Pump Chiller" + "Heat Pump Chiller", + "Missing" ] }, "HeatingPlantTypeOptions": { - "type": "string", - "enum": ["UNKNOWN_PLANT_HEATING", "NO_PLANT_HEATING", "HOTWATER_PLANT", "STEAM_PLANT"], - "descriptions": ["Unknown", "None", "Hot Water Plant", "Steam Plant"] + "enum": [ + "UNKNOWN_PLANT_HEATING", + "NO_PLANT_HEATING", + "HOTWATER_PLANT", + "STEAM_PLANT", + null + ], + "descriptions": [ + "Unknown", + "None", + "Hot Water Plant", + "Steam Plant", + "Missing" + ] }, "HeatPumpChillerHeatingSourceConditionOptions": { - "type": "string", "enum": [ "UNKNOWN_SOURCE", "AIRSOURCE_47DB_43WB", "AIRSOURCE_17DB_15WB", "WATERSOURCE_54F_44F", - "WATERSOURCE_75F_65F" + "WATERSOURCE_75F_65F", + null ], "descriptions": [ "Unknown", "Air Source 17db/15wb", "Air Source 47db/43wb", "Water Source 54F/44F", - "Water Source 75F/65F" + "Water Source 75F/65F", + "Missing" ] }, "HeatPumpChillerLeavingHeatingWaterTempOptions": { - "type": "string", - "enum": ["UNKNOWN_TEMPERATURE", "LOW", "MEDIUM", "HIGH", "BOOST"], + "enum": [ + "UNKNOWN_TEMPERATURE", + "LOW", + "MEDIUM", + "HIGH", + "BOOST", + null + ], "descriptions": [ "Unknown", "Low (105F)", "Medium (120F)", "High (140F)", - "Boost (140F)" + "Boost (140F)", + "Missing" ] }, "HeatPumpChillerTypeOptions": { - "type": "string", "enum": [ "NO_HP_CHILLER", "AIRSOURCE_HEATPUMP_CHILLER", "WATERSOURCE_POSITIVE_DISPLACEMENT_HEATPUMP_CHILLER", - "WATERSOURCE_CENTRIFUGAL_HEATPUMP_CHILLER" + "WATERSOURCE_CENTRIFUGAL_HEATPUMP_CHILLER", + null ], "descriptions": [ "Unknown", "Air Source", "Water Source Positive Displacement", - "Water Source Centrifugal" + "Water Source Centrifugal", + "Missing" ] }, "HeatRejectionTypeOptions": { - "type": "string", "enum": [ "UNKNOWN_HEAT_REJECTION_DEVICE", "NO_HEAT_REJECTION_DEVICE", @@ -5603,7 +7213,8 @@ "PROPELLER_OR_AXIAL_FAN_EVAPORATIVE_CONDENSER_AMMONIA", "CENTRIFUGAL_FAN_FAN_EVAPORATIVE_CONDENSER_R_507A", "CENTRIFUGAL_FAN_FAN_EVAPORATIVE_CONDENSER_AMMONIA", - "PROPELLER_OR_AXIAL_FAN_DRY_COOLER" + "PROPELLER_OR_AXIAL_FAN_DRY_COOLER", + null ], "descriptions": [ "Unknown", @@ -5619,28 +7230,53 @@ "Propeller or Axial Fan Evaporative Condenser R-507A", "Propeller or Axial Fan Evaporative Condenser Ammonia", "Centrifugal Fan Evaporative Condenser R-507A", - "Centrifugal Fan Evaporative Condenser Ammonia" + "Centrifugal Fan Evaporative Condenser Ammonia", + "Missing" ] }, "PlantTypeOptions": { "type": "string", - "enum": ["UNKNOWN_PLANT", "HEATING_PLANT", "COOLING_PLANT", "HEAT_PUMP_CHILLER_PLANT"], - "descriptions": ["Unknown", "Heating", "Cooling", "Heat Pump Chiller"] + "enum": [ + "UNKNOWN_PLANT", + "HEATING_PLANT", + "COOLING_PLANT", + "HEAT_PUMP_CHILLER_PLANT" + ], + "descriptions": [ + "Unknown", + "Heating", + "Cooling", + "Heat Pump Chiller" + ] }, "CompliancePathOptions": { "type": "string", - "enum": ["pathA", "pathB", "NA"], - "descriptions": ["Path A", "Path B", "N/A"] + "enum": [ + "pathA", + "pathB", + "NA", + "COMPLIANCE_PATH_A", + "COMPLIANCE_PATH_B", + "COMPLIANCE_PATH_UNKNOWN" + ], + "descriptions": [ + "Path A", + "Path B", + "N/A", + "Path A", + "Path B", + "Unknown" + ] }, "EquipmentEfficiencyRequirementExceptionOptions": { - "type": "string", "enum": [ "EFF_EXCEPTION_BOILER_RADIANT_PERIMETER", "EFF_EXCEPTION_BOILER_DWELLING_UNIT", "EFF_EXCEPTION_BOILER_RENEWABLE_RECOVERED_ENERGY", "EFF_EXCEPTION_WATER_HEATER_DWELLING_UNIT", "EFF_EXCEPTION_WATER_HEATER_RENEWABLE_RECOVERED_ENERGY", - "EFF_EXCEPTION_UNSPECIFIED" + "EFF_EXCEPTION_UNSPECIFIED", + null ], "descriptions": [ "Majority design heating load is radiant heating in the perimeter.", @@ -5648,7 +7284,8 @@ "On-site renewable energy or recovered energy for space heating.", "Water heaters installed in individual dwelling units.", "On-site renewable energy or recovered energy for service water heating.", - "Unspecified" + "Unspecified", + "Missing" ], "comment": [ "Only applicable to {[HVACPlant]}", @@ -5656,13 +7293,22 @@ "Only applicable to {[HVACPlant]}", "Only applicable to {[ServiceWaterHeatingSystem]}", "Only applicable to {[ServiceWaterHeatingSystem]}", - "Applicable to {[HVACPlant]} and {[ServiceWaterHeatingSystem]}" + "Applicable to {[HVACPlant]} and {[ServiceWaterHeatingSystem]}", + "Unknown" ] }, "FanSystemComplianceMethodOptions": { "type": "string", - "enum": ["FAN_SYSTEM_MOTOR_NAMEPLATE_HP", "FAN_SYSTEM_MOTOR_BRAKE_HP"], - "descriptions": ["Motor Nameplate HP", "Brake HP (BHP)"] + "enum": [ + "FAN_SYSTEM_MOTOR_NAMEPLATE_HP", + "FAN_SYSTEM_MOTOR_BRAKE_HP", + null + ], + "descriptions": [ + "Motor Nameplate HP", + "Brake HP (BHP)", + "Missing" + ] }, "FanEfficiencyExceptionTypeOptions": { "type": "string", @@ -5681,7 +7327,9 @@ "FAN_EFFICIENCY_EXCEPTION_TYPE_EMERGENCY", "FAN_EFFICIENCY_EXCEPTION_TYPE_HIGH_TEMPERATURE", "FAN_EFFICIENCY_EXCEPTION_TYPE_EXPLOSIVE", - "FAN_EFFICIENCY_EXCEPTION_TYPE_REVERSIBLE_TUNNEL_VENT" + "FAN_EFFICIENCY_EXCEPTION_TYPE_REVERSIBLE_TUNNEL_VENT", + "NONE", + null ], "descriptions": [ "Unspecified", @@ -5698,7 +7346,9 @@ "Emergency function only", "Moves gases > 482 F", "Explosive atmosphere only", - "Reversible for tunnel ventilation" + "Reversible for tunnel ventilation", + "None", + "Missing" ] }, "FanTypeOptions": { @@ -5711,7 +7361,14 @@ "FAN_TYPE_EXHAUST", "FAN_TYPE_FAN_POWERED_TERMINAL_UNIT" ], - "descriptions": ["Unspecified", "Supply", "Return", "Relief", "Exhaust", "FPTU"] + "descriptions": [ + "Unspecified", + "Supply", + "Return", + "Relief", + "Exhaust", + "FPTU" + ] }, "FanVolumeTypeOptions": { "type": "string", @@ -5721,7 +7378,12 @@ "FAN_VOLUME_TYPE_SINGLE_ZONE_VAV", "FAN_VOLUME_TYPE_MULTI_ZONE_VAV" ], - "descriptions": ["Unspecified", "Constant", "Single Zone VAV", "Multi Zone VAV"] + "descriptions": [ + "Unspecified", + "Constant", + "Single Zone VAV", + "Multi Zone VAV" + ] }, "PressureDropTypeOptions": { "type": "string", @@ -5765,16 +7427,26 @@ ] }, "SWHSystemDrawPatternTypeOptions": { - "type": "string", "enum": [ "NONE", "UNKNOWN_DRAW_PATTERN", "VERY_SMALL_DRAW_PATTERN", "HIGH_DRAW_PATTERN", "LOW_DRAW_PATTERN", - "MEDIUM_DRAW_PATTERN" + "MEDIUM_DRAW_PATTERN", + "NO_COOLING_EQUIPMENT", + null ], - "descriptions": ["None", "Unknown", "Very Small", "Low", "Medium", "High"] + "descriptions": [ + "None", + "Unknown", + "Very Small", + "Low", + "Medium", + "High", + "No Cooling Equipment", + "Missing" + ] }, "SWHSystemSubTypeOptions": { "type": "string", @@ -5807,14 +7479,31 @@ ] }, "SWHFuelTypeOptions": { - "type": "string", - "enum": ["UNKNOWN_FUEL", "GAS", "ELECTRIC", "OIL"], - "descriptions": ["Unknown", "Gas", "Electric", "Oil"] + "enum": [ + "UNKNOWN_FUEL", + "GAS", + "ELECTRIC", + "OIL", + null + ], + "descriptions": [ + "Unknown", + "Gas", + "Electric", + "Oil", + "Unspecified" + ] }, "EnergyCreditRenewableTypeOptions": { "type": "string", - "enum": ["SOLAR_THERMAL", "PV_ELECTRIC"], - "descriptions": ["Thermal System (Btuh)", "Electric System (Watts)"] + "enum": [ + "SOLAR_THERMAL", + "PV_ELECTRIC" + ], + "descriptions": [ + "Thermal System (Btuh)", + "Electric System (Watts)" + ] }, "GSHPFieldSourceCapacityTypeOptions": { "type": "string", @@ -6162,20 +7851,23 @@ ] }, "RenewableExceptionOptions": { - "type": "string", "enum": [ "RENEWABLE_ONSITE_EXCEPTION_NONE", "RENEWABLE_ONSITE_EXCEPTION_LOW_SOLAR_RAD", "RENEWABLE_ONSITE_EXCEPTION_ROOF_COVERED80", "RENEWABLE_ONSITE_EXCEPTION_ROOF_COVERED50", - "RENEWABLE_ONSITE_EXCEPTION_LOW_FLOOR_AREA" + "RENEWABLE_ONSITE_EXCEPTION_LOW_FLOOR_AREA", + "RENEWABLE_ONSITE_EXCEPTION_IECC2024_LOW_FLOOR_AREA", + null ], "descriptions": [ "None", "Building receives less than 1.1 kBtu/ft2 average incident solar radiation daily", "Building with more than 80% roof area covered by equipment", "Building with more than 50% roof area shaded by natural objects or structures", - "Building effective floor area is less than 10,000 ft2" + "Building effective floor area is less than 10,000 ft2", + "Building effective floor area is less than 5,000 ft2", + "Unspecified" ] }, "RenewableSystemTypeOptions": { @@ -6201,8 +7893,14 @@ }, "RenewableSystemCapacityUnitOptions": { "type": "string", - "enum": ["RENEWABLE_SYSTEM_UNIT_WATT", "RENEWABLE_SYSTEM_UNIT_BTUH"], - "descriptions": ["Watt", "Btu/h"] + "enum": [ + "RENEWABLE_SYSTEM_UNIT_WATT", + "RENEWABLE_SYSTEM_UNIT_BTUH" + ], + "descriptions": [ + "Watt", + "Btu/h" + ] }, "ActivityTypeOptions": { "type": "string", @@ -6219,6 +7917,7 @@ "ACTIVITY_COMMON_ATRIUM_ABOVE_40", "ACTIVITY_COMMON_AUDIENCE_AREA", "ACTIVITY_COMMON_AUDITORIUM", + "ACTIVITY_COMMON_CONFERENCE_CELL", "ACTIVITY_COMMON_CONFERENCE_HALL", "ACTIVITY_COMMON_CORRIDOR", "ACTIVITY_COMMON_CORRIDOR_LT_8_FEET", @@ -6232,6 +7931,7 @@ "ACTIVITY_COMMON_EXHIBITION", "ACTIVITY_COMMON_FOOD_PREP", "ACTIVITY_COMMON_GROCERY_STORE", + "ACTIVITY_COMMON_GUESTROOM", "ACTIVITY_COMMON_HOSPITAL", "ACTIVITY_COMMON_HOTEL", "ACTIVITY_COMMON_INACTIVE_STORAGE", @@ -6248,10 +7948,10 @@ "ACTIVITY_COMMON_MANUFACTURING", "ACTIVITY_COMMON_MOTEL", "ACTIVITY_COMMON_MOVIE", - "ACTIVITY_COMMON_OFFICE", "ACTIVITY_COMMON_OFFICE_ENCLOSED", "ACTIVITY_COMMON_OFFICE_OPEN", "ACTIVITY_COMMON_OTHER", + "ACTIVITY_COMMON_PATIENT", "ACTIVITY_COMMON_POLICE", "ACTIVITY_COMMON_POST", "ACTIVITY_COMMON_RELIGIOUS", @@ -6263,6 +7963,7 @@ "ACTIVITY_COMMON_THEATER", "ACTIVITY_COMMON_TRANS", "ACTIVITY_COMMON_WAREHOUSE", + "ACTIVITY_COMMON_WELLNESS_LOUNGE", "ACTIVITY_COMMON_WORKSHOP", "ACTIVITY_CONVENTION_AUDIENCE", "ACTIVITY_CONVENTION_EXHIBIT_SPACE", @@ -6271,6 +7972,10 @@ "ACTIVITY_FOOD_CAFETERIA", "ACTIVITY_FOOD_FAMILY_RESTAURANT", "ACTIVITY_FOOD_LOUNGE", + "ACTIVITY_GAME_HIGH_LIMITS_GAME", + "ACTIVITY_GAME_SLOTS", + "ACTIVITY_GAME_SPORTSBOOK", + "ACTIVITY_GAME_TABLE_GAMES", "ACTIVITY_GYM_EXERCISE", "ACTIVITY_GYM_EXERCISE_CENTER_AUDIENCE", "ACTIVITY_GYM_GYM_AUDIENCE", @@ -6295,6 +8000,7 @@ "ACTIVITY_HOSPITAL_RECOVERY", "ACTIVITY_HOSPITAL_SUPPLY", "ACTIVITY_HOSPITAL_CONTROL", + "ACTIVITY_HOSPITAL_TELEMEDICINE_ROOM", "ACTIVITY_HOTEL_DINING", "ACTIVITY_HOTEL_FUNCTION", "ACTIVITY_HOTEL_LOBBY", @@ -6337,16 +8043,23 @@ "ACTIVITY_PARKING_GARAGE_AREA", "ACTIVITY_PARKING_PEDESTRIAN", "ACTIVITY_PARKING_DAYLIGHT_ZONE", + "ACTIVITY_PARKING_DAYLIGHT_TRANSITION_ZONE", "ACTIVITY_POST_SORTING", "ACTIVITY_RELIGIOUS_AUDIENCE", "ACTIVITY_RELIGIOUS_FELLOWSHIP_HALL", "ACTIVITY_RELIGIOUS_PULPIT", "ACTIVITY_RETAIL_FITTING", "ACTIVITY_RETAIL_MALL", + "ACTIVITY_RETAIL_MASSAGE_SPACE", + "ACTIVITY_RETAIL_NAIL_SALON", + "ACTIVITY_RETAIL_NAIL_SALON_MALL", "ACTIVITY_RETAIL_SALES", + "ACTIVITY_RETAIL_HAIR_SALON", "ACTIVITY_RETAIL_HAIRCARE", "ACTIVITY_RETAIL_MASSAGE", "ACTIVITY_RETAIL_NAILCARE", + "ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_FACILITIES", + "ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_WAIT_AREA", "ACTIVITY_SPORTS_AUDIENCE", "ACTIVITY_SPORTS_COURT", "ACTIVITY_SPORTS_COURT_CLASS1", @@ -6360,6 +8073,7 @@ "ACTIVITY_THEATER_DRESSING", "ACTIVITY_TRANS_BAGGAGE", "ACTIVITY_TRANS_CONCOURSE", + "ACTIVITY_TRANS_AIRPORT_HANGER", "ACTIVITY_TRANS_SEATING", "ACTIVITY_TRANS_TERMINAL", "ACTIVITY_TRANS_AIR_HANGER", @@ -6393,6 +8107,7 @@ "ACTIVITY_SCHOOL_OFFICE", "ACTIVITY_SCHOOL_STORAGE", "ACTIVITY_TRANS_DINING", + "ACTIVITY_TRANS_PASSENGER_LOAD", "ACTIVITY_HOSPITAL_PUBLIC_STAFF_LOUNGE", "ACTIVITY_ASSISTED_LIVING_FACILITY_CHAPEL", "ACTIVITY_ASSISTED_LIVING_FACILITY_DINING", @@ -6425,12 +8140,17 @@ "ACTIVITY_MOVIE_ELEVATOR", "ACTIVITY_UNFINISHED", "ACTIVITY_COMMON_OFFICE_ENCLOSED_GT250", - "ACTIVITY_COMMON_OFFICE_ENCLOSED_GT300" + "ACTIVITY_COMMON_OFFICE_ENCLOSED_GT300", + + "ACTIVITY_SECURITY_SCREEN_GENERAL_AREA", + "ACTIVITY_SPORTS_POOL_CLASS1", + "ACTIVITY_SPORTS_POOL_CLASS2", + "ACTIVITY_SPORTS_POOL_CLASS3", + "ACTIVITY_SPORTS_POOL_CLASS4" ], "descriptions": [] }, "BallastTypeOptions": { - "type": "string", "enum": [ "ELECTRONIC", "MAGNETIC", @@ -6439,7 +8159,8 @@ "PULSE_START", "STANDARD", "PREMIUM_EFF", - "DIMMING" + "DIMMING", + null ], "descriptions": [ "Electronic", @@ -6449,15 +8170,27 @@ "Pulse start", "Standard", "Premium efficiency", - "Dimming" + "Dimming", + "Unspecified" ] }, "RequirementAnswerStatus": { - "type": "string", - "enum": ["NOT_SATISFIED", "SATISFIED", "EXCEPTION", "MULTIPLE"], - "descriptions": ["Not Satisfied", "Satisfied", "Exception", "Multiple"] + "enum": [ + "NOT_SATISFIED", + "SATISFIED", + "EXCEPTION", + "MULTIPLE", + null + ], + "descriptions": [ + "Not Satisfied", + "Satisfied", + "Exception", + "Multiple", + "Missing" + ] } }, "version": "0.0.2", "$ref": "comCheck.schema.json#/definitions/ComBuilding" -} +} \ No newline at end of file diff --git a/comcheck_api/types/core_types.py b/comcheck_api/types/core_types.py index 6258ae2..b9bbfad 100644 --- a/comcheck_api/types/core_types.py +++ b/comcheck_api/types/core_types.py @@ -1,32 +1,72 @@ # generated by datamodel-codegen: # filename: comCheck.schema.json -# timestamp: 2026-06-10T04:00:05+00:00 +# timestamp: 2026-08-11T17:22:04+00:00 from __future__ import annotations -from enum import IntEnum, StrEnum -from typing import Annotated, Any +from enum import Enum, StrEnum +from typing import Annotated, Any, Literal from comcheck_api.types.custom_base_model import CustomBaseModel from pydantic import ConfigDict, Field, RootModel +from pydantic.experimental.missing_sentinel import MISSING + + +class EfficiencyPackageType(Enum): + """ + Efficiency Package Type + """ + + EFF_PACKAGE_UNKNOWN = 'EFF_PACKAGE_UNKNOWN' + EFF_PACKAGE_HVAC_PERFORMANCE = 'EFF_PACKAGE_HVAC_PERFORMANCE' + EFF_PACKAGE_LIGHTING_REDUCED_LPD = 'EFF_PACKAGE_LIGHTING_REDUCED_LPD' + EFF_PACKAGE_REDUCED_AIR_INFILTRATION = 'EFF_PACKAGE_REDUCED_AIR_INFILTRATION' + EFF_PACKAGE_ENHANCED_ENVELOPE_PERFORMANCE = ( + 'EFF_PACKAGE_ENHANCED_ENVELOPE_PERFORMANCE' + ) + EFF_PACKAGE_ENHANCED_LIGHTING_CONTROLS = 'EFF_PACKAGE_ENHANCED_LIGHTING_CONTROLS' + EFF_PACKAGE_ONSITE_RENEWABLES = 'EFF_PACKAGE_ONSITE_RENEWABLES' + NoneType_None = None + + +class EnergyCreditMultiplierException(Enum): + """ + Energy Credit Multiplier Exception + """ + + NO_ENERGY_CREDIT_MULTIPLIER_EXCEPTION = 'NO_ENERGY_CREDIT_MULTIPLIER_EXCEPTION' + ENERGY_CREDIT_MULTIPLIER_EXCEPTION_LOW_ENERGY_BUILDINGS = ( + 'ENERGY_CREDIT_MULTIPLIER_EXCEPTION_LOW_ENERGY_BUILDINGS' + ) + ENERGY_CREDIT_MULTIPLIER_EXCEPTION_PRIMARY_HEAT_PUMP = ( + 'ENERGY_CREDIT_MULTIPLIER_EXCEPTION_PRIMARY_HEAT_PUMP' + ) + NoneType_None = None class EnergyCodeOptions(StrEnum): + CEZ_IECC2009 = 'CEZ_IECC2009' + CEZ_IECC2012 = 'CEZ_IECC2012' CEZ_IECC2015 = 'CEZ_IECC2015' CEZ_IECC2018 = 'CEZ_IECC2018' CEZ_IECC2021 = 'CEZ_IECC2021' CEZ_IECC2024 = 'CEZ_IECC2024' + CEZ_IECC2024_APPXCF = 'CEZ_IECC2024_APPXCF' + CEZ_90_1_2007 = 'CEZ_90_1_2007' + CEZ_90_1_2010 = 'CEZ_90_1_2010' CEZ_90_1_2013 = 'CEZ_90_1_2013' CEZ_90_1_2016 = 'CEZ_90_1_2016' CEZ_90_1_2019 = 'CEZ_90_1_2019' CEZ_90_1_2022 = 'CEZ_90_1_2022' + NONE = 'NONE' -class RequirementAnswerStatus(StrEnum): +class RequirementAnswerStatus(Enum): NOT_SATISFIED = 'NOT_SATISFIED' SATISFIED = 'SATISFIED' EXCEPTION = 'EXCEPTION' MULTIPLE = 'MULTIPLE' + NoneType_None = None class Project(CustomBaseModel): @@ -34,11 +74,11 @@ class Project(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING projectTitle: Annotated[str | None, Field(description='Project title')] = ( 'New Project' ) @@ -107,7 +147,7 @@ class Project(CustomBaseModel): notes: Annotated[str | None, Field(description='$comment')] = None -class WallTypeOptions(StrEnum): +class WallTypeOptionsEnum(StrEnum): WOOD_FRAME_16_AG_WALL = 'WOOD_FRAME_16_AG_WALL' WOOD_FRAME_24_AG_WALL = 'WOOD_FRAME_24_AG_WALL' METAL_FRAME_16_AG_WALL = 'METAL_FRAME_16_AG_WALL' @@ -117,6 +157,12 @@ class WallTypeOptions(StrEnum): CONCRETE_AG_WALL = 'CONCRETE_AG_WALL' MASONRY_AG_WALL = 'MASONRY_AG_WALL' OTHER_AG_WALL = 'OTHER_AG_WALL' + OTHER_BG_WALL = 'OTHER_BG_WALL' + OTHER_FRAME = 'OTHER_FRAME' + + +class WallTypeOptions(RootModel[WallTypeOptionsEnum | None | MISSING]): + root: WallTypeOptionsEnum | None | MISSING = MISSING class Location(CustomBaseModel): @@ -124,11 +170,11 @@ class Location(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING state: Annotated[ str, Field( @@ -145,7 +191,7 @@ class Location(CustomBaseModel): climateZone: Annotated[int, Field(description='Climate zone number', ge=1, le=8)] -class BgWallTypeOptions(StrEnum): +class BgWallTypeOptions(Enum): SOLID_CONCRETE_LE_8IN_BG_WALL = 'SOLID_CONCRETE_LE_8IN_BG_WALL' SOLID_CONCRETE_GT_8IN_BG_WALL = 'SOLID_CONCRETE_GT_8IN_BG_WALL' CMU_LE_8IN_EMPTY_CELLS_BG_WALL = 'CMU_LE_8IN_EMPTY_CELLS_BG_WALL' @@ -156,6 +202,7 @@ class BgWallTypeOptions(StrEnum): WOOD_BG_WALL = 'WOOD_BG_WALL' MASONRY_BG_WALL = 'MASONRY_BG_WALL' OTHER_BG_WALL = 'OTHER_BG_WALL' + NoneType_None = None class AdjacentSpaceTypeOptions(StrEnum): @@ -181,6 +228,7 @@ class WholeBuildingTypeOptions(StrEnum): WHOLE_BUILDING_HEALTH_CLINIC = 'WHOLE_BUILDING_HEALTH_CLINIC' WHOLE_BUILDING_HOSPITAL = 'WHOLE_BUILDING_HOSPITAL' WHOLE_BUILDING_HOTEL = 'WHOLE_BUILDING_HOTEL' + WHOLE_BUILDING_INVALID_USE = 'WHOLE_BUILDING_INVALID_USE' WHOLE_BUILDING_LIBRARY = 'WHOLE_BUILDING_LIBRARY' WHOLE_BUILDING_MANUFACTURING = 'WHOLE_BUILDING_MANUFACTURING' WHOLE_BUILDING_MOTEL = 'WHOLE_BUILDING_MOTEL' @@ -207,7 +255,7 @@ class WholeBuildingTypeOptions(StrEnum): WHOLE_BUILDING_WORKSHOP = 'WHOLE_BUILDING_WORKSHOP' -class OrientationOptions(StrEnum): +class OrientationOptions(Enum): NORTH = 'NORTH' EAST = 'EAST' SOUTH = 'SOUTH' @@ -217,15 +265,18 @@ class OrientationOptions(StrEnum): SOUTHWEST = 'SOUTHWEST' SOUTHEAST = 'SOUTHEAST' UNSPECIFIED_ORIENTATION = 'UNSPECIFIED_ORIENTATION' + NoneType_None = None -class EnvelopeAssemblyAllowanceTypeOptions(StrEnum): +class EnvelopeAssemblyAllowanceTypeOptions(Enum): ENV_ALLOWANCE_NONE = 'ENV_ALLOWANCE_NONE' ENV_ALLOWANCE_FEN_SIX_FEET_ABOVE_FLOOR = 'ENV_ALLOWANCE_FEN_SIX_FEET_ABOVE_FLOOR' ENV_ALLOWANCE_SKYLIGHT_HAS_DAYLIGHT_AND_AUTOCONTROLS = ( 'ENV_ALLOWANCE_SKYLIGHT_HAS_DAYLIGHT_AND_AUTOCONTROLS' ) ENV_ALLOWANCE_DYNAMIC_GLAZING = 'ENV_ALLOWANCE_DYNAMIC_GLAZING' + NONE = 'NONE' + NoneType_None = None class ConstructionTypeOptions(StrEnum): @@ -249,6 +300,10 @@ class AltExemptTypeOptions(StrEnum): EXEMPT_GLAZING_UNIT_REPLACEMENT_LT_25_PCT = ( 'EXEMPT_GLAZING_UNIT_REPLACEMENT_LT_25_PCT' ) + EXEMPT_HISTORIC_CHARACTERISTIC = 'EXEMPT_HISTORIC_CHARACTERISTIC' + EXEMPT_LIGHTING_SPACE_REPLACEMENT_LT_20_PCT_LOAD = ( + 'EXEMPT_LIGHTING_SPACE_REPLACEMENT_LT_20_PCT_LOAD' + ) EXEMPT_LIGHTING_SPACE_REPLACEMENT_LT_50_PCT = ( 'EXEMPT_LIGHTING_SPACE_REPLACEMENT_LT_50_PCT' ) @@ -291,7 +346,7 @@ class AltExemptTypeOptions(StrEnum): ) -class CondenserTypeOptions(StrEnum): +class CondenserTypeOptions(Enum): UNKNOWN_CONDENSER = 'UNKNOWN_CONDENSER' NO_CONDENSER = 'NO_CONDENSER' AIR_COOLED = 'AIR_COOLED' @@ -302,6 +357,7 @@ class CondenserTypeOptions(StrEnum): AIR_COOLED_FAD_CONDENSER = 'AIR_COOLED_FAD_CONDENSER' AIR_COOLED_DUCTED_CONDENSER = 'AIR_COOLED_DUCTED_CONDENSER' CHILLED_WATER = 'CHILLED_WATER' + NoneType_None = None class ActivityTypeOptions(StrEnum): @@ -319,6 +375,7 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_COMMON_ATRIUM_ABOVE_40 = 'ACTIVITY_COMMON_ATRIUM_ABOVE_40' ACTIVITY_COMMON_AUDIENCE_AREA = 'ACTIVITY_COMMON_AUDIENCE_AREA' ACTIVITY_COMMON_AUDITORIUM = 'ACTIVITY_COMMON_AUDITORIUM' + ACTIVITY_COMMON_CONFERENCE_CELL = 'ACTIVITY_COMMON_CONFERENCE_CELL' ACTIVITY_COMMON_CONFERENCE_HALL = 'ACTIVITY_COMMON_CONFERENCE_HALL' ACTIVITY_COMMON_CORRIDOR = 'ACTIVITY_COMMON_CORRIDOR' ACTIVITY_COMMON_CORRIDOR_LT_8_FEET = 'ACTIVITY_COMMON_CORRIDOR_LT_8_FEET' @@ -332,6 +389,7 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_COMMON_EXHIBITION = 'ACTIVITY_COMMON_EXHIBITION' ACTIVITY_COMMON_FOOD_PREP = 'ACTIVITY_COMMON_FOOD_PREP' ACTIVITY_COMMON_GROCERY_STORE = 'ACTIVITY_COMMON_GROCERY_STORE' + ACTIVITY_COMMON_GUESTROOM = 'ACTIVITY_COMMON_GUESTROOM' ACTIVITY_COMMON_HOSPITAL = 'ACTIVITY_COMMON_HOSPITAL' ACTIVITY_COMMON_HOTEL = 'ACTIVITY_COMMON_HOTEL' ACTIVITY_COMMON_INACTIVE_STORAGE = 'ACTIVITY_COMMON_INACTIVE_STORAGE' @@ -348,10 +406,10 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_COMMON_MANUFACTURING = 'ACTIVITY_COMMON_MANUFACTURING' ACTIVITY_COMMON_MOTEL = 'ACTIVITY_COMMON_MOTEL' ACTIVITY_COMMON_MOVIE = 'ACTIVITY_COMMON_MOVIE' - ACTIVITY_COMMON_OFFICE = 'ACTIVITY_COMMON_OFFICE' ACTIVITY_COMMON_OFFICE_ENCLOSED = 'ACTIVITY_COMMON_OFFICE_ENCLOSED' ACTIVITY_COMMON_OFFICE_OPEN = 'ACTIVITY_COMMON_OFFICE_OPEN' ACTIVITY_COMMON_OTHER = 'ACTIVITY_COMMON_OTHER' + ACTIVITY_COMMON_PATIENT = 'ACTIVITY_COMMON_PATIENT' ACTIVITY_COMMON_POLICE = 'ACTIVITY_COMMON_POLICE' ACTIVITY_COMMON_POST = 'ACTIVITY_COMMON_POST' ACTIVITY_COMMON_RELIGIOUS = 'ACTIVITY_COMMON_RELIGIOUS' @@ -363,6 +421,7 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_COMMON_THEATER = 'ACTIVITY_COMMON_THEATER' ACTIVITY_COMMON_TRANS = 'ACTIVITY_COMMON_TRANS' ACTIVITY_COMMON_WAREHOUSE = 'ACTIVITY_COMMON_WAREHOUSE' + ACTIVITY_COMMON_WELLNESS_LOUNGE = 'ACTIVITY_COMMON_WELLNESS_LOUNGE' ACTIVITY_COMMON_WORKSHOP = 'ACTIVITY_COMMON_WORKSHOP' ACTIVITY_CONVENTION_AUDIENCE = 'ACTIVITY_CONVENTION_AUDIENCE' ACTIVITY_CONVENTION_EXHIBIT_SPACE = 'ACTIVITY_CONVENTION_EXHIBIT_SPACE' @@ -371,6 +430,10 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_FOOD_CAFETERIA = 'ACTIVITY_FOOD_CAFETERIA' ACTIVITY_FOOD_FAMILY_RESTAURANT = 'ACTIVITY_FOOD_FAMILY_RESTAURANT' ACTIVITY_FOOD_LOUNGE = 'ACTIVITY_FOOD_LOUNGE' + ACTIVITY_GAME_HIGH_LIMITS_GAME = 'ACTIVITY_GAME_HIGH_LIMITS_GAME' + ACTIVITY_GAME_SLOTS = 'ACTIVITY_GAME_SLOTS' + ACTIVITY_GAME_SPORTSBOOK = 'ACTIVITY_GAME_SPORTSBOOK' + ACTIVITY_GAME_TABLE_GAMES = 'ACTIVITY_GAME_TABLE_GAMES' ACTIVITY_GYM_EXERCISE = 'ACTIVITY_GYM_EXERCISE' ACTIVITY_GYM_EXERCISE_CENTER_AUDIENCE = 'ACTIVITY_GYM_EXERCISE_CENTER_AUDIENCE' ACTIVITY_GYM_GYM_AUDIENCE = 'ACTIVITY_GYM_GYM_AUDIENCE' @@ -395,6 +458,7 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_HOSPITAL_RECOVERY = 'ACTIVITY_HOSPITAL_RECOVERY' ACTIVITY_HOSPITAL_SUPPLY = 'ACTIVITY_HOSPITAL_SUPPLY' ACTIVITY_HOSPITAL_CONTROL = 'ACTIVITY_HOSPITAL_CONTROL' + ACTIVITY_HOSPITAL_TELEMEDICINE_ROOM = 'ACTIVITY_HOSPITAL_TELEMEDICINE_ROOM' ACTIVITY_HOTEL_DINING = 'ACTIVITY_HOTEL_DINING' ACTIVITY_HOTEL_FUNCTION = 'ACTIVITY_HOTEL_FUNCTION' ACTIVITY_HOTEL_LOBBY = 'ACTIVITY_HOTEL_LOBBY' @@ -441,16 +505,29 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_PARKING_GARAGE_AREA = 'ACTIVITY_PARKING_GARAGE_AREA' ACTIVITY_PARKING_PEDESTRIAN = 'ACTIVITY_PARKING_PEDESTRIAN' ACTIVITY_PARKING_DAYLIGHT_ZONE = 'ACTIVITY_PARKING_DAYLIGHT_ZONE' + ACTIVITY_PARKING_DAYLIGHT_TRANSITION_ZONE = ( + 'ACTIVITY_PARKING_DAYLIGHT_TRANSITION_ZONE' + ) ACTIVITY_POST_SORTING = 'ACTIVITY_POST_SORTING' ACTIVITY_RELIGIOUS_AUDIENCE = 'ACTIVITY_RELIGIOUS_AUDIENCE' ACTIVITY_RELIGIOUS_FELLOWSHIP_HALL = 'ACTIVITY_RELIGIOUS_FELLOWSHIP_HALL' ACTIVITY_RELIGIOUS_PULPIT = 'ACTIVITY_RELIGIOUS_PULPIT' ACTIVITY_RETAIL_FITTING = 'ACTIVITY_RETAIL_FITTING' ACTIVITY_RETAIL_MALL = 'ACTIVITY_RETAIL_MALL' + ACTIVITY_RETAIL_MASSAGE_SPACE = 'ACTIVITY_RETAIL_MASSAGE_SPACE' + ACTIVITY_RETAIL_NAIL_SALON = 'ACTIVITY_RETAIL_NAIL_SALON' + ACTIVITY_RETAIL_NAIL_SALON_MALL = 'ACTIVITY_RETAIL_NAIL_SALON_MALL' ACTIVITY_RETAIL_SALES = 'ACTIVITY_RETAIL_SALES' + ACTIVITY_RETAIL_HAIR_SALON = 'ACTIVITY_RETAIL_HAIR_SALON' ACTIVITY_RETAIL_HAIRCARE = 'ACTIVITY_RETAIL_HAIRCARE' ACTIVITY_RETAIL_MASSAGE = 'ACTIVITY_RETAIL_MASSAGE' ACTIVITY_RETAIL_NAILCARE = 'ACTIVITY_RETAIL_NAILCARE' + ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_FACILITIES = ( + 'ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_FACILITIES' + ) + ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_WAIT_AREA = ( + 'ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_WAIT_AREA' + ) ACTIVITY_SPORTS_AUDIENCE = 'ACTIVITY_SPORTS_AUDIENCE' ACTIVITY_SPORTS_COURT = 'ACTIVITY_SPORTS_COURT' ACTIVITY_SPORTS_COURT_CLASS1 = 'ACTIVITY_SPORTS_COURT_CLASS1' @@ -464,6 +541,7 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_THEATER_DRESSING = 'ACTIVITY_THEATER_DRESSING' ACTIVITY_TRANS_BAGGAGE = 'ACTIVITY_TRANS_BAGGAGE' ACTIVITY_TRANS_CONCOURSE = 'ACTIVITY_TRANS_CONCOURSE' + ACTIVITY_TRANS_AIRPORT_HANGER = 'ACTIVITY_TRANS_AIRPORT_HANGER' ACTIVITY_TRANS_SEATING = 'ACTIVITY_TRANS_SEATING' ACTIVITY_TRANS_TERMINAL = 'ACTIVITY_TRANS_TERMINAL' ACTIVITY_TRANS_AIR_HANGER = 'ACTIVITY_TRANS_AIR_HANGER' @@ -497,6 +575,7 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_SCHOOL_OFFICE = 'ACTIVITY_SCHOOL_OFFICE' ACTIVITY_SCHOOL_STORAGE = 'ACTIVITY_SCHOOL_STORAGE' ACTIVITY_TRANS_DINING = 'ACTIVITY_TRANS_DINING' + ACTIVITY_TRANS_PASSENGER_LOAD = 'ACTIVITY_TRANS_PASSENGER_LOAD' ACTIVITY_HOSPITAL_PUBLIC_STAFF_LOUNGE = 'ACTIVITY_HOSPITAL_PUBLIC_STAFF_LOUNGE' ACTIVITY_ASSISTED_LIVING_FACILITY_CHAPEL = ( 'ACTIVITY_ASSISTED_LIVING_FACILITY_CHAPEL' @@ -548,6 +627,11 @@ class ActivityTypeOptions(StrEnum): ACTIVITY_UNFINISHED = 'ACTIVITY_UNFINISHED' ACTIVITY_COMMON_OFFICE_ENCLOSED_GT250 = 'ACTIVITY_COMMON_OFFICE_ENCLOSED_GT250' ACTIVITY_COMMON_OFFICE_ENCLOSED_GT300 = 'ACTIVITY_COMMON_OFFICE_ENCLOSED_GT300' + ACTIVITY_SECURITY_SCREEN_GENERAL_AREA = 'ACTIVITY_SECURITY_SCREEN_GENERAL_AREA' + ACTIVITY_SPORTS_POOL_CLASS1 = 'ACTIVITY_SPORTS_POOL_CLASS1' + ACTIVITY_SPORTS_POOL_CLASS2 = 'ACTIVITY_SPORTS_POOL_CLASS2' + ACTIVITY_SPORTS_POOL_CLASS3 = 'ACTIVITY_SPORTS_POOL_CLASS3' + ACTIVITY_SPORTS_POOL_CLASS4 = 'ACTIVITY_SPORTS_POOL_CLASS4' class ExteriorUseTypeOptions(StrEnum): @@ -589,7 +673,7 @@ class ExteriorUseTypeOptions(StrEnum): TOTAL_EXTERIOR_USES = 'TOTAL_EXTERIOR_USES' -class AdvancedControlsAllowanceTypeOptions(StrEnum): +class AdvancedControlsAllowanceTypeOptions(Enum): ALLOWANCE_ADV_CONTROLS_NOT_SPECIFIED = 'ALLOWANCE_ADV_CONTROLS_NOT_SPECIFIED' ALLOWANCE_ADV_CONTROLS_DIM_MANUAL_CONT = 'ALLOWANCE_ADV_CONTROLS_DIM_MANUAL_CONT' ALLOWANCE_ADV_CONTROLS_DIM_PROG_MULTI_LEVEL = ( @@ -608,6 +692,7 @@ class AdvancedControlsAllowanceTypeOptions(StrEnum): 'ALLOWANCE_ADV_CONTROLS_OCC_SENSORS_WORKSTATION_CONT_DIM_PLUS_MANUAL_DIM' ) ALLOWANCE_ADV_CONTROLS_AUTO_BILEVEL = 'ALLOWANCE_ADV_CONTROLS_AUTO_BILEVEL' + NoneType_None = None class LightingTypeOptions(StrEnum): @@ -622,17 +707,34 @@ class LightingTypeOptions(StrEnum): OTHER_LIGHTING_TYPE = 'OTHER_LIGHTING_TYPE' -class BoilerDraftTypeOptions(StrEnum): +class BoilerDraftTypeOptions(Enum): NATURAL_DRAFT = 'NATURAL_DRAFT' FORCED_DRAFT = 'FORCED_DRAFT' + NoneType_None = None + + +class HasPressureDropCredits(Enum): + """ + Flag indicates if the fan system has pressure drop credits + """ + + int_0 = 0 + int_1 = 1 + NoneType_None = None -class FanSystemComplianceMethodOptions(StrEnum): +class FanSystemComplianceMethodOptionsEnum(StrEnum): FAN_SYSTEM_MOTOR_NAMEPLATE_HP = 'FAN_SYSTEM_MOTOR_NAMEPLATE_HP' FAN_SYSTEM_MOTOR_BRAKE_HP = 'FAN_SYSTEM_MOTOR_BRAKE_HP' -class FanEfficiencyExceptionTypeOptions(StrEnum): +class FanSystemComplianceMethodOptions( + RootModel[FanSystemComplianceMethodOptionsEnum | None | MISSING] +): + root: FanSystemComplianceMethodOptionsEnum | None | MISSING = MISSING + + +class FanEfficiencyExceptionTypeOptionsEnum(StrEnum): FAN_EFFICIENCY_EXCEPTION_TYPE_UNSPECIFIED = ( 'FAN_EFFICIENCY_EXCEPTION_TYPE_UNSPECIFIED' ) @@ -672,6 +774,13 @@ class FanEfficiencyExceptionTypeOptions(StrEnum): FAN_EFFICIENCY_EXCEPTION_TYPE_REVERSIBLE_TUNNEL_VENT = ( 'FAN_EFFICIENCY_EXCEPTION_TYPE_REVERSIBLE_TUNNEL_VENT' ) + NONE = 'NONE' + + +class FanEfficiencyExceptionTypeOptions( + RootModel[FanEfficiencyExceptionTypeOptionsEnum | None | MISSING] +): + root: FanEfficiencyExceptionTypeOptionsEnum | None | MISSING = MISSING class PressureDropTypeOptions(StrEnum): @@ -712,13 +821,25 @@ class PressureDropTypeOptions(StrEnum): ) -class SWHSystemDrawPatternTypeOptions(StrEnum): +class HeatPumpPoolHeater(Enum): + """ + Flag identifies whether the SWH uses heat pump to heat the pool. - Only used when poolSystem is true. False as default + """ + + int_0 = 0 + int_1 = 1 + NoneType_None = None + + +class SWHSystemDrawPatternTypeOptions(Enum): NONE = 'NONE' UNKNOWN_DRAW_PATTERN = 'UNKNOWN_DRAW_PATTERN' VERY_SMALL_DRAW_PATTERN = 'VERY_SMALL_DRAW_PATTERN' HIGH_DRAW_PATTERN = 'HIGH_DRAW_PATTERN' LOW_DRAW_PATTERN = 'LOW_DRAW_PATTERN' MEDIUM_DRAW_PATTERN = 'MEDIUM_DRAW_PATTERN' + NO_COOLING_EQUIPMENT = 'NO_COOLING_EQUIPMENT' + NoneType_None = None class EnergyCreditRenewableTypeOptions(StrEnum): @@ -726,7 +847,7 @@ class EnergyCreditRenewableTypeOptions(StrEnum): PV_ELECTRIC = 'PV_ELECTRIC' -class RenewableExceptionOptions(StrEnum): +class RenewableExceptionOptions(Enum): RENEWABLE_ONSITE_EXCEPTION_NONE = 'RENEWABLE_ONSITE_EXCEPTION_NONE' RENEWABLE_ONSITE_EXCEPTION_LOW_SOLAR_RAD = ( 'RENEWABLE_ONSITE_EXCEPTION_LOW_SOLAR_RAD' @@ -740,6 +861,10 @@ class RenewableExceptionOptions(StrEnum): RENEWABLE_ONSITE_EXCEPTION_LOW_FLOOR_AREA = ( 'RENEWABLE_ONSITE_EXCEPTION_LOW_FLOOR_AREA' ) + RENEWABLE_ONSITE_EXCEPTION_IECC2024_LOW_FLOOR_AREA = ( + 'RENEWABLE_ONSITE_EXCEPTION_IECC2024_LOW_FLOOR_AREA' + ) + NoneType_None = None class RenewableSystemTypeOptions(StrEnum): @@ -844,10 +969,12 @@ class ThermalBridgeTypeOptions(StrEnum): THERMAL_BRIDGE_WALL_TO_PLANE_TRANSITION = 'THERMAL_BRIDGE_WALL_TO_PLANE_TRANSITION' -class ProjectTypeOptions(StrEnum): +class ProjectTypeOptions(Enum): ADDITION = 'ADDITION' ALTERATION = 'ALTERATION' NEW_CONSTRUCTION = 'NEW_CONSTRUCTION' + NONE = 'NONE' + NoneType_None = None class ProjectSubTypeOptions(StrEnum): @@ -883,6 +1010,10 @@ class StateRegionEnergyCodeOptions(StrEnum): CEZ_ONTARIO = 'CEZ_ONTARIO' CEZ_DC2017 = 'CEZ_DC2017' CEZ_PUERTO_RICO = 'CEZ_PUERTO_RICO' + CEZ_NYS2024_IECC2024 = 'CEZ_NYS2024_IECC2024' + CEZ_NYS2025_9012022 = 'CEZ_NYS2025_9012022' + CEZ_NYC2025_IECC2024 = 'CEZ_NYC2025_IECC2024' + CEZ_NYC2025_9012022 = 'CEZ_NYC2025_9012022' CEZ_NYSTRETCH_90_1_2016 = 'CEZ_NYSTRETCH_90_1_2016' CEZ_NYSTRETCH_NYC_90_1_2016 = 'CEZ_NYSTRETCH_NYC_90_1_2016' CEZ_NYSTRETCH_90_1_11_G = 'CEZ_NYSTRETCH_90_1_11_G' @@ -890,6 +1021,7 @@ class StateRegionEnergyCodeOptions(StrEnum): CEZ_CHICAGO_IECC2022 = 'CEZ_CHICAGO_IECC2022' CEZ_FL = 'CEZ_FL' CEZ_VT = 'CEZ_VT' + CEZ_VT2024_IECC2021 = 'CEZ_VT2024_IECC2021' CEZ_NY = 'CEZ_NY' CEZ_NEWYORKCITY = 'CEZ_NEWYORKCITY' CEZ_NYSTRETCH_NYC_IECC2018 = 'CEZ_NYSTRETCH_NYC_IECC2018' @@ -897,10 +1029,12 @@ class StateRegionEnergyCodeOptions(StrEnum): CEZ_CO_BOULDER = 'CEZ_CO_BOULDER' CEZ_CO_DENVER = 'CEZ_CO_DENVER' CEZ_CO_DENVER_90_1_2016 = 'CEZ_CO_DENVER_90_1_2016' + CEZ_LA2021_IECC2021 = 'CEZ_LA2021_IECC2021' CEZ_MN = 'CEZ_MN' CEZ_MAS = 'CEZ_MAS' CEZ_MAS_STRETCH_IECC2021 = 'CEZ_MAS_STRETCH_IECC2021' CEZ_CO_DENVER_IECC2021 = 'CEZ_CO_DENVER_IECC2021' + NONE = 'NONE' class ComplianceModeOptions(StrEnum): @@ -910,6 +1044,7 @@ class ComplianceModeOptions(StrEnum): class AirBarrierComplianceTypeOptions(StrEnum): + AIR_BARRIER_OPTION_CONTINUITY_PLAN = 'AIR_BARRIER_OPTION_CONTINUITY_PLAN' AIR_BARRIER_OPTION_UNKNOWN = 'AIR_BARRIER_OPTION_UNKNOWN' AIR_BARRIER_OPTION_MATERIAL_PERM = 'AIR_BARRIER_OPTION_MATERIAL_PERM' AIR_BARRIER_OPTION_ASSEMBLY_PERM = 'AIR_BARRIER_OPTION_ASSEMBLY_PERM' @@ -920,6 +1055,7 @@ class RoofTypeOptions(StrEnum): ABOVE_DECK_ROOF = 'ABOVE_DECK_ROOF' METAL_BLDG_STANDING_SEAM_ROOF = 'METAL_BLDG_STANDING_SEAM_ROOF' METAL_BLDG_SCREW_DOWN_ROOF = 'METAL_BLDG_SCREW_DOWN_ROOF' + METAL_ROOF_W_THERMAL_BREAK = 'METAL_ROOF_W_THERMAL_BREAK' WOOD_STD_JOIST_TRUSS = 'WOOD_STD_JOIST_TRUSS' NON_WOOD_JOIST_TRUSS = 'NON_WOOD_JOIST_TRUSS' OTHER_ROOF = 'OTHER_ROOF' @@ -939,6 +1075,7 @@ class RoofInsulationTypeOptions(StrEnum): class HighAlbedoRoofReqTypeOptions(StrEnum): HA_ROOF_REQ_UNSPECIFIED = 'HA_ROOF_REQ_UNSPECIFIED' + HA_ROOF_REQ_SOLAR_REFLECTANCE = 'HA_ROOF_REQ_SOLAR_REFLECTANCE' HA_ROOF_REQ_SOLAR_REFLECTANCE_INDEX = 'HA_ROOF_REQ_SOLAR_REFLECTANCE_INDEX' HA_ROOF_REQ_SOLAR_REFLECTANCE_INDEX_THERMAL_EMITTANCE = ( 'HA_ROOF_REQ_SOLAR_REFLECTANCE_INDEX_THERMAL_EMITTANCE' @@ -964,13 +1101,14 @@ class OtherRoofTypeOptions(StrEnum): ROOF_OTHER_OTHER = 'ROOF_OTHER_OTHER' -class FloorTypeOptions(StrEnum): +class FloorTypeOptions(Enum): ALL_WOOD_JOIST_TRUSS_FLOOR = 'ALL_WOOD_JOIST_TRUSS_FLOOR' NON_WOOD_JOIST_TRUSS_FLOOR = 'NON_WOOD_JOIST_TRUSS_FLOOR' STRUCTURAL_SLAB_FLOOR = 'STRUCTURAL_SLAB_FLOOR' HEATED_SLAB_ON_GRADE = 'HEATED_SLAB_ON_GRADE' UNHEATED_SLAB_ON_GRADE = 'UNHEATED_SLAB_ON_GRADE' OTHER_FLOOR = 'OTHER_FLOOR' + NoneType_None = None class FloorExposedFrameType(StrEnum): @@ -984,9 +1122,20 @@ class SlabInsulationPositionOptions(StrEnum): HORIZONTAL = 'HORIZONTAL' VERTICAL = 'VERTICAL' NO_INSULATION = 'NO_INSULATION' + NONE = 'NONE' class AgWallConstructionDetailsTypeOptions(StrEnum): + AG_WALL_CONSTRUCTION_DETAILS_UNKNOWN = 'AG_WALL_CONSTRUCTION_DETAILS_UNKNOWN' + AG_WALL_CONSTRUCTION_DETAILS_HORIZONTAL_Z_GIRTS = ( + 'AG_WALL_CONSTRUCTION_DETAILS_HORIZONTAL_Z_GIRTS' + ) + AG_WALL_CONSTRUCTION_DETAILS_VERTICAL_Z_GIRTS = ( + 'AG_WALL_CONSTRUCTION_DETAILS_VERTICAL_Z_GIRTS' + ) + AG_WALL_CONSTRUCTION_DETAILS_Z_GIRTS_THERMAL_BROKEN = ( + 'AG_WALL_CONSTRUCTION_DETAILS_Z_GIRTS_THERMAL_BROKEN' + ) AG_WALL_METAL_BLDG_SINGLE_LAYER_MINERAL_FIBER = ( 'AG_WALL_METAL_BLDG_SINGLE_LAYER_MINERAL_FIBER' ) @@ -1021,35 +1170,49 @@ class AgWallOtherTypeOptions(StrEnum): NONE = 'NONE' -class CMUTypeOptions(StrEnum): +class CMUTypeOptions(Enum): CMU_SOLID_GROUT = 'CMU_SOLID_GROUT' CMU_PARTIAL_GROUT_CELLS_EMPTY = 'CMU_PARTIAL_GROUT_CELLS_EMPTY' CMU_PARTIAL_GROUT_CELLS_INSULATED = 'CMU_PARTIAL_GROUT_CELLS_INSULATED' CMU_UNREINFORCED_CELLS_EMPTY = 'CMU_UNREINFORCED_CELLS_EMPTY' CMU_UNREINFORCED_CELLS_INSULATED = 'CMU_UNREINFORCED_CELLS_INSULATED' - - -class ConcreteDensityOptions(IntEnum): - integer_0 = 0 - integer_95 = 95 - integer_115 = 115 - integer_144 = 144 - - -class ConcreteThicknessOptions(IntEnum): - integer_0 = 0 - integer_6 = 6 - integer_8 = 8 - integer_10 = 10 - integer_12 = 12 + NONE = 'NONE' + NoneType_None = None + + +class ConcreteDensityOptions(Enum): + int_0 = 0 + int_85 = 85 + int_95 = 95 + int_115 = 115 + int_135 = 135 + int_144 = 144 + NoneType_None = None + + +class ConcreteThicknessOptions(Enum): + int_0 = 0 + int_3 = 3 + int_4 = 4 + int_5 = 5 + int_6 = 6 + int_7 = 7 + int_8 = 8 + int_9 = 9 + int_10 = 10 + int_11 = 11 + int_12 = 12 + NoneType_None = None class EnvelopeAssemblyExemptionOptions(StrEnum): ENV_EXEMPTION_NONE = 'ENV_EXEMPTION_NONE' ENV_EXEMPTION_DYNAMIC_GLAZING = 'ENV_EXEMPTION_DYNAMIC_GLAZING' + NONE = 'NONE' class FurringTypeOptions(StrEnum): + NONE = 'NONE' WOOD_FURRING = 'WOOD_FURRING' METAL_FURRING = 'METAL_FURRING' NO_FURRING = 'NO_FURRING' @@ -1062,22 +1225,37 @@ class InsulationPositionOptions(StrEnum): NO_INSULATION = 'NO_INSULATION' -class FenestrationFrameTypeOptions(StrEnum): +class FenestrationFrameTypeOptions(Enum): METAL = 'METAL' METAL_W_THERMAL_BREAK = 'METAL_W_THERMAL_BREAK' WOOD = 'WOOD' VINYL = 'VINYL' CURTAIN_WALL = 'CURTAIN_WALL' CURTAIN_WALL_STOREFRONT = 'CURTAIN_WALL_STOREFRONT' + NON_METAL = 'NON_METAL' + NONE = 'NONE' OTHER_FRAME = 'OTHER_FRAME' + METAL_FRAME_24_AG_WALL = 'METAL_FRAME_24_AG_WALL' + GLASS_DOOR = 'GLASS_DOOR' + METAL_THERMAL_BREAK = 'METAL_THERMAL_BREAK' + OTHER_DOOR = 'OTHER_DOOR' + INSUL_METAL_DOOR = 'INSUL_METAL_DOOR' + NO_INSUL_SINGLE_METAL_DOOR = 'NO_INSUL_SINGLE_METAL_DOOR' + WOOD_FRAME_16_AG_WALL = 'WOOD_FRAME_16_AG_WALL' + ALL_WOOD_JOIST_TRUSS_FLOOR = 'ALL_WOOD_JOIST_TRUSS_FLOOR' + METAL_FRAME_16_AG_WALL = 'METAL_FRAME_16_AG_WALL' + WOOD_DOOR = 'WOOD_DOOR' + NoneType_None = None class GlazingTypeOptions(StrEnum): SINGLE_PANE = 'SINGLE_PANE' DOUBLE_PANE = 'DOUBLE_PANE' DOUBLE_PANE_LOWE = 'DOUBLE_PANE_LOWE' + OTHER_GLAZING = 'OTHER_GLAZING' TRIPLE_PANE = 'TRIPLE_PANE' TRIPLE_PANE_LOWE = 'TRIPLE_PANE_LOWE' + NONE = 'NONE' class SolarTypeOptions(StrEnum): @@ -1085,6 +1263,7 @@ class SolarTypeOptions(StrEnum): TINTED = 'TINTED' REFLECTIVE = 'REFLECTIVE' OTHER_SOLAR = 'OTHER_SOLAR' + NONE = 'NONE' class WindowProductionTypeOptions(StrEnum): @@ -1093,9 +1272,11 @@ class WindowProductionTypeOptions(StrEnum): class PerfDataTypeOptions(StrEnum): + NONE = 'NONE' PERF_TYPE_NFRC = 'PERF_TYPE_NFRC' PERF_TYPE_OTHER = 'PERF_TYPE_OTHER' PERF_TYPE_DEFAULT = 'PERF_TYPE_DEFAULT' + PERF_TYPE_UNSPECIFIED = 'PERF_TYPE_UNSPECIFIED' class WindowOpenTypeOptions(StrEnum): @@ -1111,6 +1292,7 @@ class SkylightCurbTypeOptions(StrEnum): class GlazingMaterialTypeOptions(StrEnum): GLASS_GLAZING_MAT = 'GLASS_GLAZING_MAT' PLASTIC_GLAZING_MAT = 'PLASTIC_GLAZING_MAT' + NONE = 'NONE' class DoorTypeOptions(StrEnum): @@ -1119,6 +1301,7 @@ class DoorTypeOptions(StrEnum): INSUL_METAL_DOOR = 'INSUL_METAL_DOOR' WOOD_DOOR = 'WOOD_DOOR' GLASS_DOOR = 'GLASS_DOOR' + METAL_W_THERMAL_BREAK = 'METAL_W_THERMAL_BREAK' OTHER_DOOR = 'OTHER_DOOR' UPWARD_ACTING_SECTIONAL = 'UPWARD_ACTING_SECTIONAL' @@ -1140,10 +1323,14 @@ class DoorGlazingFrameTypeOptions(StrEnum): NON_METAL = 'NON_METAL' -class LightingAllowanceTypeOptions(StrEnum): +class LightingAllowanceTypeOptions(Enum): ALLOWANCE_NONE = 'ALLOWANCE_NONE' + ALLOWANCE_ADVANCED_CONTROLS = 'ALLOWANCE_ADVANCED_CONTROLS' ALLOWANCE_DECORATIVE_APPEARANCE = 'ALLOWANCE_DECORATIVE_APPEARANCE' + ALLOWANCE_DECORATIVE_APPEARANCE_LOBBIES = 'ALLOWANCE_DECORATIVE_APPEARANCE_LOBBIES' + ALLOWANCE_DECORATIVE_APPEARANCE_OTHER = 'ALLOWANCE_DECORATIVE_APPEARANCE_OTHER' ALLOWANCE_DISPLAY_TERMINAL = 'ALLOWANCE_DISPLAY_TERMINAL' + ALLOWANCE_ELECTRICAL_MECHANICAL = 'ALLOWANCE_ELECTRICAL_MECHANICAL' ALLOWANCE_VEHICLE_SPORT_ELECT_HIGHLIGHT = 'ALLOWANCE_VEHICLE_SPORT_ELECT_HIGHLIGHT' ALLOWANCE_FURNITURE_CLOTHES_COSMETIC_HIGHLIGHT = ( 'ALLOWANCE_FURNITURE_CLOTHES_COSMETIC_HIGHLIGHT' @@ -1152,32 +1339,56 @@ class LightingAllowanceTypeOptions(StrEnum): 'ALLOWANCE_JEWELRY_CRYSTAL_CHINA_HIGHLIGHT' ) ALLOWANCE_OTHER_HIGHLIGHT = 'ALLOWANCE_OTHER_HIGHLIGHT' + ALLOWANCE_VIDEO_CONFERENCE = 'ALLOWANCE_VIDEO_CONFERENCE' + NONE = 'NONE' + NoneType_None = None class LightingExemptionTypeOptions(StrEnum): EXEMPTION_NONE = 'EXEMPTION_NONE' EXEMPTION_ADVERTISING_OR_DIRECTION_SIGN = 'EXEMPTION_ADVERTISING_OR_DIRECTION_SIGN' EXEMPTION_ATHLETIC_PLAY_AREA = 'EXEMPTION_ATHLETIC_PLAY_AREA' + EXEMPTION_APPROVED_SAFETY = 'EXEMPTION_APPROVED_SAFETY' EXEMPTION_CASINO_GAMING = 'EXEMPTION_CASINO_GAMING' EXEMPTION_DRESSING_ROOM_MIRROR = 'EXEMPTION_DRESSING_ROOM_MIRROR' + EXEMPTION_DWELL_UNIT_CONTROLLED = 'EXEMPTION_DWELL_UNIT_CONTROLLED' EXEMPTION_EDUCATION = 'EXEMPTION_EDUCATION' + EXEMPTION_EMERGENCY_AUTOOFF = 'EXEMPTION_EMERGENCY_AUTOOFF' EXEMPTION_EMERGENCY_LIGHT = 'EXEMPTION_EMERGENCY_LIGHT' EXEMPTION_EQUIPMENT = 'EXEMPTION_EQUIPMENT' EXEMPTION_EXIT_SIGN = 'EXEMPTION_EXIT_SIGN' EXEMPTION_FOOD_PREPERATION = 'EXEMPTION_FOOD_PREPERATION' EXEMPTION_FURNITURE_SUPPLEMENTAL = 'EXEMPTION_FURNITURE_SUPPLEMENTAL' + EXEMPTION_HIGHLIGHT_HAZARDS = 'EXEMPTION_HIGHLIGHT_HAZARDS' EXEMPTION_HIGHLIGHT_LANDMARK = 'EXEMPTION_HIGHLIGHT_LANDMARK' + EXEMPTION_INDUSTRIAL_PRODUCTION = 'EXEMPTION_INDUSTRIAL_PRODUCTION' + EXEMPTION_MANUFACTURER_AS_PART_OF_EQUIP = 'EXEMPTION_MANUFACTURER_AS_PART_OF_EQUIP' EXEMPTION_MEDICAL_PROCEDURE = 'EXEMPTION_MEDICAL_PROCEDURE' EXEMPTION_PARKING_GARAGE_TRANSITION = 'EXEMPTION_PARKING_GARAGE_TRANSITION' EXEMPTION_PLANT = 'EXEMPTION_PLANT' + EXEMPTION_POOLS_WATER = 'EXEMPTION_POOLS_WATER' EXEMPTION_REFRIGERATOR = 'EXEMPTION_REFRIGERATOR' EXEMPTION_RELIGIOUS_PULPIT_CHOIR = 'EXEMPTION_RELIGIOUS_PULPIT_CHOIR' + EXEMPTION_REQUIRED_EGRESS = 'EXEMPTION_REQUIRED_EGRESS' EXEMPTION_RETAIL_DISPLAY = 'EXEMPTION_RETAIL_DISPLAY' + EXEMPTION_TEMP_LIGHTING = 'EXEMPTION_TEMP_LIGHTING' EXEMPTION_THEATER_APPLICATION = 'EXEMPTION_THEATER_APPLICATION' + EXEMPTION_THEME_PARK_ELEMENTS = 'EXEMPTION_THEME_PARK_ELEMENTS' EXEMPTION_HEALTH_SAFETY_REG = 'EXEMPTION_HEALTH_SAFETY_REG' + EXEMPTION_HIGHLIGHT_MONUMENT = 'EXEMPTION_HIGHLIGHT_MONUMENT' + EXEMPTION_TRANSPORTATION_MARKER = 'EXEMPTION_TRANSPORTATION_MARKER' + EXEMPTION_TRANSPORTATION_SITE = 'EXEMPTION_TRANSPORTATION_SITE' + EXEMPTION_EMERGENCY_LIGHT_OFF_NORMAL_BUSINESS_HRS = ( + 'EXEMPTION_EMERGENCY_LIGHT_OFF_NORMAL_BUSINESS_HRS' + ) + EXEMPTION_MUSEUM_DISPLAY = 'EXEMPTION_MUSEUM_DISPLAY' + EXEMPTION_SEARCHLIGHTS = 'EXEMPTION_SEARCHLIGHTS' + EXEMPTION_SLEEPING_UNIT = 'EXEMPTION_SLEEPING_UNIT' + EXEMPTION_VISUALLY_IMPAIRED = 'EXEMPTION_VISUALLY_IMPAIRED' -class TrackLightingWattageBasisTypeOptions(StrEnum): +class TrackLightingWattageBasisTypeOptionsEnum(StrEnum): + NONE = 'NONE' WATTAGE_BASIS_NOT_SET = 'WATTAGE_BASIS_NOT_SET' TRACK_BASIS = 'TRACK_BASIS' CIRCUIT_BREAKER_CAPACITY = 'CIRCUIT_BREAKER_CAPACITY' @@ -1185,6 +1396,12 @@ class TrackLightingWattageBasisTypeOptions(StrEnum): TRANSFORMER_CAPACITY = 'TRANSFORMER_CAPACITY' +class TrackLightingWattageBasisTypeOptions( + RootModel[TrackLightingWattageBasisTypeOptionsEnum | None | MISSING] +): + root: TrackLightingWattageBasisTypeOptionsEnum | None | MISSING = MISSING + + class ExteriorLightingZoneTypeOptions(StrEnum): EXT_ZONE_UNSPECIFIED = 'EXT_ZONE_UNSPECIFIED' EXT_ZONE_RESIDENTIAL = 'EXT_ZONE_RESIDENTIAL' @@ -1196,6 +1413,7 @@ class ExteriorLightingZoneTypeOptions(StrEnum): EXT_ZONE_FOREST = 'EXT_ZONE_FOREST' EXT_ZONE_RURAL = 'EXT_ZONE_RURAL' EXT_ZONE_OTHER = 'EXT_ZONE_OTHER' + EXT_ZONE_UNDEVELOPED = 'EXT_ZONE_UNDEVELOPED' class ThermalBridgeCategoryOptions(StrEnum): @@ -1204,10 +1422,11 @@ class ThermalBridgeCategoryOptions(StrEnum): THERMAL_BRIDGE_POINT = 'THERMAL_BRIDGE_POINT' -class ThermalBridgeComplianceTypeOptions(StrEnum): +class ThermalBridgeComplianceTypeOptions(Enum): THERMAL_BRIDGE_NON_PRESCRIPTIVE = 'THERMAL_BRIDGE_NON_PRESCRIPTIVE' THERMAL_BRIDGE_PRESCRIPTIVE = 'THERMAL_BRIDGE_PRESCRIPTIVE' - field__THERMAL_BRIDGE_AS_DESIGNED = ' THERMAL_BRIDGE_AS_DESIGNED' + THERMAL_BRIDGE_AS_DESIGNED = 'THERMAL_BRIDGE_AS_DESIGNED' + NoneType_None = None class ThermalBridgeExceptionTypeOptions(StrEnum): @@ -1279,6 +1498,7 @@ class EconomizerTypeOptions(StrEnum): NO_ECONOMIZER = 'NO_ECONOMIZER' AIR_ECONOMIZER = 'AIR_ECONOMIZER' WATER_ECONOMIZER = 'WATER_ECONOMIZER' + FLUID_ECONOMIZER = 'FLUID_ECONOMIZER' class EconomizerExceptionOptions(StrEnum): @@ -1307,15 +1527,17 @@ class FanControlOptions(StrEnum): OTHER_FAN_CONTROL = 'OTHER_FAN_CONTROL' -class FuelTypeOptions(StrEnum): +class FuelTypeOptions(Enum): UNKNOWN_FUEL = 'UNKNOWN_FUEL' GAS = 'GAS' ELECTRIC = 'ELECTRIC' OIL = 'OIL' + OIL_RESIDUAL = 'OIL_RESIDUAL' PROPANE = 'PROPANE' HOTWATER = 'HOTWATER' STEAM = 'STEAM' OTHER_FOSSIL_FUEL = 'OTHER_FOSSIL_FUEL' + NoneType_None = None class SpaceHeatingSystemExceptionOptions(StrEnum): @@ -1410,48 +1632,54 @@ class HeatPumpTypeOptions(StrEnum): ) -class ChillerTypeOptions(StrEnum): +class ChillerTypeOptions(Enum): NO_CHILLER = 'NO_CHILLER' CENTRIFUGAL = 'CENTRIFUGAL' + CENTRIFUGAL_NON_STANDARD = 'CENTRIFUGAL_NON_STANDARD' ROTARY_SCREW_OR_SCROLL = 'ROTARY_SCREW_OR_SCROLL' RECIPROCATING = 'RECIPROCATING' SINGLE_EFFECT_ABSORPTION = 'SINGLE_EFFECT_ABSORPTION' DOUBLE_EFFECT_ABSORPTION_DIRECT_FIRED = 'DOUBLE_EFFECT_ABSORPTION_DIRECT_FIRED' DOUBLE_EFFECT_ABSORPTION_INDIRECT_FIRED = 'DOUBLE_EFFECT_ABSORPTION_INDIRECT_FIRED' + NoneType_None = None -class CoolingPlantTypeOptions(StrEnum): +class CoolingPlantTypeOptions(Enum): UNKNOWN_PLANT_COOLING = 'UNKNOWN_PLANT_COOLING' NO_PLANT_COOLING = 'NO_PLANT_COOLING' CONDENSER_UNIT = 'CONDENSER_UNIT' WATER_CHILLER = 'WATER_CHILLER' HEATPUMP_CHILLER = 'HEATPUMP_CHILLER' + NoneType_None = None -class HeatingPlantTypeOptions(StrEnum): +class HeatingPlantTypeOptions(Enum): UNKNOWN_PLANT_HEATING = 'UNKNOWN_PLANT_HEATING' NO_PLANT_HEATING = 'NO_PLANT_HEATING' HOTWATER_PLANT = 'HOTWATER_PLANT' STEAM_PLANT = 'STEAM_PLANT' + NoneType_None = None -class HeatPumpChillerHeatingSourceConditionOptions(StrEnum): +class HeatPumpChillerHeatingSourceConditionOptions(Enum): UNKNOWN_SOURCE = 'UNKNOWN_SOURCE' AIRSOURCE_47DB_43WB = 'AIRSOURCE_47DB_43WB' AIRSOURCE_17DB_15WB = 'AIRSOURCE_17DB_15WB' WATERSOURCE_54F_44F = 'WATERSOURCE_54F_44F' WATERSOURCE_75F_65F = 'WATERSOURCE_75F_65F' + NoneType_None = None -class HeatPumpChillerLeavingHeatingWaterTempOptions(StrEnum): +class HeatPumpChillerLeavingHeatingWaterTempOptions(Enum): UNKNOWN_TEMPERATURE = 'UNKNOWN_TEMPERATURE' LOW = 'LOW' MEDIUM = 'MEDIUM' HIGH = 'HIGH' BOOST = 'BOOST' + NoneType_None = None -class HeatPumpChillerTypeOptions(StrEnum): +class HeatPumpChillerTypeOptions(Enum): NO_HP_CHILLER = 'NO_HP_CHILLER' AIRSOURCE_HEATPUMP_CHILLER = 'AIRSOURCE_HEATPUMP_CHILLER' WATERSOURCE_POSITIVE_DISPLACEMENT_HEATPUMP_CHILLER = ( @@ -1460,9 +1688,10 @@ class HeatPumpChillerTypeOptions(StrEnum): WATERSOURCE_CENTRIFUGAL_HEATPUMP_CHILLER = ( 'WATERSOURCE_CENTRIFUGAL_HEATPUMP_CHILLER' ) + NoneType_None = None -class HeatRejectionTypeOptions(StrEnum): +class HeatRejectionTypeOptions(Enum): UNKNOWN_HEAT_REJECTION_DEVICE = 'UNKNOWN_HEAT_REJECTION_DEVICE' NO_HEAT_REJECTION_DEVICE = 'NO_HEAT_REJECTION_DEVICE' AIR_COOLED_CONDENSER = 'AIR_COOLED_CONDENSER' @@ -1496,6 +1725,7 @@ class HeatRejectionTypeOptions(StrEnum): 'CENTRIFUGAL_FAN_FAN_EVAPORATIVE_CONDENSER_AMMONIA' ) PROPELLER_OR_AXIAL_FAN_DRY_COOLER = 'PROPELLER_OR_AXIAL_FAN_DRY_COOLER' + NoneType_None = None class PlantTypeOptions(StrEnum): @@ -1509,9 +1739,12 @@ class CompliancePathOptions(StrEnum): pathA = 'pathA' pathB = 'pathB' NA = 'NA' + COMPLIANCE_PATH_A = 'COMPLIANCE_PATH_A' + COMPLIANCE_PATH_B = 'COMPLIANCE_PATH_B' + COMPLIANCE_PATH_UNKNOWN = 'COMPLIANCE_PATH_UNKNOWN' -class EquipmentEfficiencyRequirementExceptionOptions(StrEnum): +class EquipmentEfficiencyRequirementExceptionOptions(Enum): EFF_EXCEPTION_BOILER_RADIANT_PERIMETER = 'EFF_EXCEPTION_BOILER_RADIANT_PERIMETER' EFF_EXCEPTION_BOILER_DWELLING_UNIT = 'EFF_EXCEPTION_BOILER_DWELLING_UNIT' EFF_EXCEPTION_BOILER_RENEWABLE_RECOVERED_ENERGY = ( @@ -1524,6 +1757,7 @@ class EquipmentEfficiencyRequirementExceptionOptions(StrEnum): 'EFF_EXCEPTION_WATER_HEATER_RENEWABLE_RECOVERED_ENERGY' ) EFF_EXCEPTION_UNSPECIFIED = 'EFF_EXCEPTION_UNSPECIFIED' + NoneType_None = None class FanTypeOptions(StrEnum): @@ -1558,11 +1792,12 @@ class SWHSystemTypeOptions(StrEnum): STORAGE_WATER_HEATER = 'STORAGE_WATER_HEATER' -class SWHFuelTypeOptions(StrEnum): +class SWHFuelTypeOptions(Enum): UNKNOWN_FUEL = 'UNKNOWN_FUEL' GAS = 'GAS' ELECTRIC = 'ELECTRIC' OIL = 'OIL' + NoneType_None = None class GSHPFieldSourceCapacityTypeOptions(StrEnum): @@ -1992,7 +2227,7 @@ class RenewableSystemCapacityUnitOptions(StrEnum): RENEWABLE_SYSTEM_UNIT_BTUH = 'RENEWABLE_SYSTEM_UNIT_BTUH' -class BallastTypeOptions(StrEnum): +class BallastTypeOptions(Enum): ELECTRONIC = 'ELECTRONIC' MAGNETIC = 'MAGNETIC' HYBRID = 'HYBRID' @@ -2001,6 +2236,7 @@ class BallastTypeOptions(StrEnum): STANDARD = 'STANDARD' PREMIUM_EFF = 'PREMIUM_EFF' DIMMING = 'DIMMING' + NoneType_None = None class Control(CustomBaseModel): @@ -2008,22 +2244,22 @@ class Control(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING version: Annotated[ str | None, Field(description='Software version - shall always set to an empty string'), - ] = '' + ] code: Annotated[ EnergyCodeOptions | StateRegionEnergyCodeOptions, Field(description='Energy code types'), ] complianceMode: Annotated[ - ComplianceModeOptions | None, Field(description='Project compliance type') - ] = 'UA' + ComplianceModeOptions, Field(description='Project compliance type') + ] class Requirements(CustomBaseModel): @@ -2031,26 +2267,22 @@ class Requirements(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None - category: Annotated[ - str | None, Field(description='Requirement Answer - category') - ] = '' - requirementName: Annotated[ - str | None, Field(description='Requirement Answer - name') - ] = '' + ] = MISSING + category: Annotated[str, Field(description='Requirement Answer - category')] + requirementName: Annotated[str, Field(description='Requirement Answer - name')] status: Annotated[ - RequirementAnswerStatus | None, Field(description='Requirement Answer - status') - ] = '' + RequirementAnswerStatus, Field(description='Requirement Answer - status') + ] locationOnPlans: Annotated[ str | None, Field(description='Requirement Answer - Location On Plans') - ] = '' + ] exceptionName: Annotated[ str | None, Field(description='Requirement Answer - Exception Name') - ] = '' + ] class Window(CustomBaseModel): @@ -2058,93 +2290,90 @@ class Window(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING bldgUseKey: Annotated[ - str, Field(description='key reference of the building use area data group') + str | None, + Field(description='key reference of the building use area data group'), ] - description: Annotated[ - str | None, Field(description='The name of the component') - ] = '' + description: Annotated[str | None, Field(description='The name of the component')] adjacentSpaceType: Annotated[ AdjacentSpaceTypeOptions | None, Field(description='Space type of the adjacent space'), - ] = None + ] adjacentSpaceBuildingType: Annotated[ - WholeBuildingTypeOptions | None, + WholeBuildingTypeOptions | MISSING, Field(description='Building type of the adjacent space'), - ] = None - assemblyType: Annotated[ - str | None, Field(description='The type of the component') - ] = 'Window' + ] = MISSING + assemblyType: Annotated[str, Field(description='The type of the component')] propUValue: Annotated[ - float | None, - Field(description='Proposed thermal transmittance of the window.', ge=0.0), - ] = 0.0 - grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] = 0.0 + float | None, Field(description='Proposed thermal transmittance of the window.') + ] + grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING propShgc: Annotated[ float | None, Field(description='Proposed solar heat gain coefficient', ge=0.0) - ] = 0.0 + ] propProjectionFactor: Annotated[ float | None, Field(description='Proposed window projection factor', ge=0.0) - ] = 0.0 + ] frameType: Annotated[ - FenestrationFrameTypeOptions | None, Field(description='Window frame type') - ] = None + FenestrationFrameTypeOptions, Field(description='Window frame type') + ] glazingType: Annotated[ GlazingTypeOptions | None, Field( description='Glazing type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)' ), - ] = None - propVt: Annotated[float | None, Field(ge=0.0)] = None - preAltPropShgc: Annotated[float | None, Field(ge=0.0)] = 0.0 + ] + propVt: Annotated[float | None, Field(ge=0.0)] + preAltPropShgc: Annotated[float | None, Field(ge=0.0)] solarType: Annotated[ SolarTypeOptions | None, Field( description='Solar coating type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)' ), - ] = None - orientation: OrientationOptions | None = 'UNSPECIFIED_ORIENTATION' + ] + orientation: OrientationOptions glazingMaterialType: Annotated[ GlazingMaterialTypeOptions | None, Field(description='Glazing material type') - ] = None + ] productType: Annotated[ WindowProductionTypeOptions | None, Field(description='Product Type') ] = None allowanceType: Annotated[ - EnvelopeAssemblyAllowanceTypeOptions | None, Field(description='allowance type') - ] = None - exemptionType: EnvelopeAssemblyExemptionOptions | None = None - constructionType: Annotated[ - ConstructionTypeOptions | None, - Field(description='Construction types - compliance code specification'), - ] = None - isSiteShading: Annotated[bool | None, Field(description='Is the site shaded')] = ( + EnvelopeAssemblyAllowanceTypeOptions, Field(description='allowance type') + ] + exemptionType: EnvelopeAssemblyExemptionOptions | None + feetAg: Annotated[float | None, Field(description='Feet above grade', ge=0.0)] = ( None ) + constructionType: Annotated[ + ConstructionTypeOptions | None | MISSING, + Field(description='Construction types - compliance code specification'), + ] = MISSING + isSiteShading: Annotated[bool | None, Field(description='Is the site shaded')] perfDataType: Annotated[ PerfDataTypeOptions | None, Field(description='Performance data type option') ] = None - productId: Annotated[str | None, Field(description='Product ID')] = None - preAltPropUval: Annotated[float | None, Field(ge=0.0)] = 0.0 + productId: Annotated[str | None, Field(description='Product ID')] + preAltPropUval: Annotated[float | None | MISSING, Field(ge=0.0)] = MISSING windowOpenType: Annotated[ WindowOpenTypeOptions | None, Field(description='Window open type') - ] = None - cavityRValue: Annotated[float | None, Field(ge=0.0)] = 0.0 + ] + cavityRValue: float | None continuousRValue: Annotated[ float | None, Field( - description='Continuous insulation on the door. Can be exterior or interior or both.', - ge=0.0, + description='Continuous insulation on the door. Can be exterior or interior or both.' ), - ] = 0.0 + ] class Door(CustomBaseModel): @@ -2152,97 +2381,91 @@ class Door(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING bldgUseKey: Annotated[ - str, Field(description='key reference of the building use area data group') + str | None, + Field(description='key reference of the building use area data group'), ] - description: Annotated[ - str | None, Field(description='The name of the component') - ] = '' - assemblyType: Annotated[ - str | None, Field(description='The type of the component') - ] = 'Door' + description: Annotated[str | None, Field(description='The name of the component')] + assemblyType: Annotated[str, Field(description='The type of the component')] adjacentSpaceType: Annotated[ AdjacentSpaceTypeOptions | None, Field(description='Space type of the adjacent space'), - ] = None + ] adjacentSpaceBuildingType: Annotated[ - WholeBuildingTypeOptions | None, + WholeBuildingTypeOptions | MISSING, Field(description='Building type of the adjacent space'), - ] = None + ] = MISSING propUValue: Annotated[ - float | None, - Field(description='Proposed thermal transmittance of the window.', ge=0.0), - ] = 0.0 - grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] = None + float | None, Field(description='Proposed thermal transmittance of the window.') + ] + grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING propShgc: Annotated[ float | None, Field(description='Proposed solar heat gain coefficient', ge=0.0) - ] = 0.0 + ] propProjectionFactor: Annotated[ float | None, Field(description='Proposed window projection factor', ge=0.0) - ] = 0.0 + ] frameType: Annotated[ - FenestrationFrameTypeOptions | None, Field(description='Glass door frame type') - ] = None + FenestrationFrameTypeOptions, Field(description='Glass door frame type') + ] glazingType: Annotated[ GlazingTypeOptions | None, Field( description='Glazing type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)' ), - ] = None - propVt: Annotated[float | None, Field(ge=0.0)] = None - preAltPropShgc: Annotated[float | None, Field(ge=0.0)] = 0.0 + ] + propVt: Annotated[float | None, Field(ge=0.0)] + preAltPropShgc: Annotated[float | None, Field(ge=0.0)] solarType: Annotated[ SolarTypeOptions | None, Field( description='Solar coating type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)' ), - ] = None - orientation: OrientationOptions | None = 'UNSPECIFIED_ORIENTATION' + ] + orientation: OrientationOptions glazingMaterialType: Annotated[ GlazingMaterialTypeOptions | None, Field(description='Glazing material type') - ] = None + ] productType: Annotated[ WindowProductionTypeOptions | None, Field(description='Product Type') - ] = None + ] allowanceType: Annotated[ - EnvelopeAssemblyAllowanceTypeOptions | None, Field(description='allowance type') - ] = None - exemptionType: EnvelopeAssemblyExemptionOptions | None = None + EnvelopeAssemblyAllowanceTypeOptions, Field(description='allowance type') + ] + exemptionType: EnvelopeAssemblyExemptionOptions | None constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None - isSiteShading: Annotated[bool | None, Field(description='Is the site shaded')] = ( - None - ) + ] = MISSING + isSiteShading: Annotated[bool | None, Field(description='Is the site shaded')] perfDataType: Annotated[ PerfDataTypeOptions | None, Field(description='Performance data type option') - ] = None - productId: Annotated[str | None, Field(description='Product ID')] = None - preAltPropUval: Annotated[float | None, Field(ge=0.0)] = 0.0 - doorType: Annotated[DoorTypeOptions | None, Field(description='Door types')] = None + ] + productId: Annotated[str | None, Field(description='Product ID')] + preAltPropUval: Annotated[float | None | MISSING, Field(ge=0.0)] = MISSING + doorType: Annotated[DoorTypeOptions | None, Field(description='Door types')] doorOpenType: Annotated[ DoorOpenTypeOptions | None, Field(description='Door open types') - ] = None + ] doorEntranceType: Annotated[ DoorEntranceTypeOptions | None, Field(description='Door entrance types') - ] = None - cavityRValue: Annotated[float | None, Field(ge=0.0)] = 0.0 + ] + cavityRValue: float | None continuousRValue: Annotated[ float | None, Field( - description='Continuous insulation on the door. Can be exterior or interior or both.', - ge=0.0, + description='Continuous insulation on the door. Can be exterior or interior or both.' ), - ] = 0.0 + ] class Skylight(CustomBaseModel): @@ -2250,54 +2473,63 @@ class Skylight(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING bldgUseKey: Annotated[ - str, Field(description='key reference of the building use area data group') + str | None, + Field(description='key reference of the building use area data group'), ] - description: Annotated[ - str | None, Field(description='The name of the component') - ] = '' - assemblyType: Annotated[ - str | None, Field(description='The type of the component') - ] = 'Skylight' + cavityRValue: Annotated[ + float | None, + Field( + description='Average insulation R-value in the cavity between two studs.' + ), + ] = 0.0 + continuousRValue: Annotated[ + float | None, + Field( + description='Continuous insulation on the skylight. Can be exterior or interior or both.' + ), + ] = 0.0 + description: Annotated[str | None, Field(description='The name of the component')] + assemblyType: Annotated[str, Field(description='The type of the component')] adjacentSpaceType: Annotated[ AdjacentSpaceTypeOptions | None, Field(description='Space type of the adjacent space'), - ] = None + ] adjacentSpaceBuildingType: Annotated[ - WholeBuildingTypeOptions | None, + WholeBuildingTypeOptions | MISSING, Field(description='Building type of the adjacent space'), - ] = None + ] = MISSING propUValue: Annotated[ - float | None, - Field(description='Proposed thermal transmittance of the window.', ge=0.0), - ] = 0.0 - grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] = 0.0 - orientation: OrientationOptions | None = 'UNSPECIFIED_ORIENTATION' + float | None, Field(description='Proposed thermal transmittance of the window.') + ] + grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] + orientation: OrientationOptions altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING propShgc: Annotated[ float | None, Field(description='Proposed solar heat gain coefficient', ge=0.0) - ] = 0.0 + ] propProjectionFactor: Annotated[ float | None, Field(description='Proposed window projection factor', ge=0.0) - ] = 0.0 + ] frameType: Annotated[ - FenestrationFrameTypeOptions | None, Field(description='Window frame type') - ] = None + FenestrationFrameTypeOptions, Field(description='Window frame type') + ] glazingType: Annotated[ GlazingTypeOptions | None, Field( description='Glazing type - used when performance data option is energy code default (PERF_TYPE_DEFAULT)' ), - ] = None - propVt: Annotated[float | None, Field(ge=0.0)] = None - preAltPropShgc: Annotated[float | None, Field(ge=0.0)] = 0.0 + ] + propVt: Annotated[float | None, Field(ge=0.0)] + preAltPropShgc: Annotated[float | None, Field(ge=0.0)] solarType: Annotated[ SolarTypeOptions | None, Field( @@ -2306,29 +2538,27 @@ class Skylight(CustomBaseModel): ] = None glazingMaterialType: Annotated[ GlazingMaterialTypeOptions | None, Field(description='Glazing material type') - ] = None + ] productType: Annotated[ WindowProductionTypeOptions | None, Field(description='Product Type') - ] = None + ] allowanceType: Annotated[ - EnvelopeAssemblyAllowanceTypeOptions | None, Field(description='allowance type') - ] = None - exemptionType: EnvelopeAssemblyExemptionOptions | None = None + EnvelopeAssemblyAllowanceTypeOptions, Field(description='allowance type') + ] + exemptionType: EnvelopeAssemblyExemptionOptions | None constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None - isSiteShading: Annotated[bool | None, Field(description='Is the site shaded')] = ( - None - ) + ] = MISSING + isSiteShading: Annotated[bool | None, Field(description='Is the site shaded')] perfDataType: Annotated[ PerfDataTypeOptions | None, Field(description='Performance data type option') - ] = None - productId: Annotated[str | None, Field(description='Product ID')] = None - preAltPropUval: Annotated[float | None, Field(ge=0.0)] = 0.0 + ] + productId: Annotated[str | None, Field(description='Product ID')] + preAltPropUval: Annotated[float | None | MISSING, Field(ge=0.0)] = MISSING curbType: Annotated[ SkylightCurbTypeOptions | None, Field(description='Skylight curb type') - ] = None + ] class Roof(CustomBaseModel): @@ -2336,57 +2566,51 @@ class Roof(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None - description: Annotated[ - str | None, Field(description='The name of the component') - ] = '' - assemblyType: Annotated[ - str | None, Field(description='The type of the component') - ] = 'Roof' + ] = MISSING + description: Annotated[str | None, Field(description='The name of the component')] + assemblyType: Annotated[str, Field(description='The type of the component')] bldgUseKey: Annotated[ - str, Field(description='key reference of the building use area data group') + str | None, + Field(description='key reference of the building use area data group'), ] adjacentSpaceType: Annotated[ AdjacentSpaceTypeOptions | None, Field(description='Space type of the adjacent space'), - ] = None + ] adjacentSpaceBuildingType: Annotated[ - WholeBuildingTypeOptions | None, + WholeBuildingTypeOptions | MISSING, Field(description='Building type of the adjacent space'), - ] = None + ] = MISSING allowanceType: Annotated[ - EnvelopeAssemblyAllowanceTypeOptions | None, Field(description='allowance type') - ] = None + EnvelopeAssemblyAllowanceTypeOptions, Field(description='allowance type') + ] constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None - exemptionType: EnvelopeAssemblyExemptionOptions | None = None - orientation: OrientationOptions | None = 'UNSPECIFIED_ORIENTATION' + ] = MISSING + exemptionType: EnvelopeAssemblyExemptionOptions | None + orientation: OrientationOptions skylight: Annotated[list[Skylight], Field(description='Skylights on the roof')] - cavityRValue: Annotated[float | None, Field(ge=0.0)] = 0.0 + cavityRValue: float | None continuousRValue: Annotated[ float | None, Field( - description='Continuous insulation on the above grade wall. Can be exterior or interior or both.', - ge=0.0, + description='Continuous insulation on the above grade wall. Can be exterior or interior or both.' ), - ] = 0.0 + ] propUValue: Annotated[ float | None, - Field( - description='Proposed thermal transmittance of the above grade wall.', - ge=0.0, - ), - ] = 0.0 + Field(description='Proposed thermal transmittance of the above grade wall.'), + ] altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None - grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] = 0.0 + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING + grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] roofType: Annotated[RoofTypeOptions | None, Field(description='roof type')] = None highAlbedoRoofReqType: Annotated[ HighAlbedoRoofReqTypeOptions | None, @@ -2402,19 +2626,15 @@ class Roof(CustomBaseModel): ] = None roofInsulType: Annotated[ RoofInsulationTypeOptions | None, Field(description='roof insulation types') - ] = None - solarReflectance: Annotated[ - float | None, Field(description='solar reflectance', ge=0.0) - ] = 0.0 + ] + solarReflectance: Annotated[float, Field(description='solar reflectance', ge=0.0)] solarReflectanceIndex: Annotated[ - float | None, Field(description='solar reflectance index', ge=0.0) - ] = 0.0 - thermalEmittance: Annotated[ - float | None, Field(description='thermal emittance', ge=0.0) - ] = 0.0 + float, Field(description='solar reflectance index', ge=0.0) + ] + thermalEmittance: Annotated[float, Field(description='thermal emittance', ge=0.0)] purlinSpacing: Annotated[ float | None, Field(description='Roof purlin spacing', ge=0.0) - ] = 0.0 + ] class Floor(CustomBaseModel): @@ -2422,78 +2642,81 @@ class Floor(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING description: Annotated[str | None, Field(description='The name of the component')] assemblyType: Annotated[str, Field(description='The type of the component')] bldgUseKey: Annotated[ - str, Field(description='key reference of the building use area data group') + str | None, + Field(description='key reference of the building use area data group'), ] adjacentSpaceType: Annotated[ - AdjacentSpaceTypeOptions | None, + AdjacentSpaceTypeOptions | None | MISSING, Field(description='Space type of the adjacent space'), - ] = None + ] = MISSING allowanceType: Annotated[ - EnvelopeAssemblyAllowanceTypeOptions | None, Field(description='allowance type') - ] = None + EnvelopeAssemblyAllowanceTypeOptions | MISSING, + Field(description='allowance type'), + ] = MISSING constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None - exemptionType: EnvelopeAssemblyExemptionOptions | None = None - orientation: OrientationOptions | None = None - cavityRValue: Annotated[float | None, Field(ge=0.0)] = None + ] = MISSING + exemptionType: EnvelopeAssemblyExemptionOptions | None | MISSING = MISSING + orientation: OrientationOptions | MISSING = MISSING + cavityRValue: Annotated[float | None | MISSING, Field(ge=0.0)] = MISSING continuousRValue: Annotated[ - float | None, + float | None | MISSING, Field( description='Continuous insulation on the above grade wall. Can be exterior or interior or both.', ge=0.0, ), - ] = None + ] = MISSING propUValue: Annotated[ - float | None, - Field( - description='Proposed thermal transmittance of the above grade wall.', - ge=0.0, - ), - ] = None + float | None | MISSING, + Field(description='Proposed thermal transmittance of the above grade wall.'), + ] = MISSING altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None - grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] = None - floorType: Annotated[FloorTypeOptions | None, Field(description='Floor type')] = ( - None - ) + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING + grossArea: Annotated[ + float | None | MISSING, Field(description='gross area', ge=0.0) + ] = MISSING + floorType: Annotated[ + FloorTypeOptions | MISSING, Field(description='Floor type') + ] = MISSING depthOfInsulation: Annotated[ - int | None, + int | None | MISSING, Field( description='depth of insulation, it only works on a certain numbers including 1, 2, 3, 4, 93, 94, 95 ,96, 97, 98, 99. Mapping is in the $comment', ge=0, ), - ] = None + ] = MISSING slabFullInsulBelowMinRValue: Annotated[ - float | None, + float | None | MISSING, Field( description='Full insulation R value below the slab -> the number if fixed based on the selection of depthOfInsulation', ge=0.0, ), - ] = None + ] = MISSING insulationPosition: Annotated[ - SlabInsulationPositionOptions | None, + SlabInsulationPositionOptions | None | MISSING, Field(description='The position of the insulation'), - ] = None + ] = MISSING hasEdgeInsul: Annotated[ - bool | None, + bool | None | MISSING, Field( description='A boolean to indicate whether the bldg use area uses edge insulation' ), - ] = None + ] = MISSING floorExposedFrameType: Annotated[ - FloorExposedFrameType | None, Field(description='Floor Exposed Frame type') - ] = None + FloorExposedFrameType | None | MISSING, + Field(description='Floor Exposed Frame type'), + ] = MISSING class HVACSystem(CustomBaseModel): @@ -2501,14 +2724,15 @@ class HVACSystem(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING condenser: Annotated[ CondenserTypeOptions | None, Field(description='condenser type') ] = 'UNKNOWN_CONDENSER' @@ -2560,96 +2784,107 @@ class HVACSystem(CustomBaseModel): 'UNKNOWN_FUEL' ) spaceHeatingSystemException: Annotated[ - SpaceHeatingSystemExceptionOptions | None, + SpaceHeatingSystemExceptionOptions | None | MISSING, Field( description='Exceptions for electric space heating system - Denver 2022 requirement. Can be null for other codes' ), - ] = None + ] = MISSING heatingEquipCapacity: Annotated[ float | None, Field(description='Heating equipment capacity', ge=0.0) ] = 0.0 heatingEquipment: Annotated[ - HeatingEquipmentTypeOptions | None, Field(description='Heating equipment type') - ] = None + HeatingEquipmentTypeOptions | MISSING, + Field(description='Heating equipment type'), + ] = MISSING heatPump: Annotated[ - HeatPumpTypeOptions | None, Field(description='Heat pump type') - ] = None + HeatPumpTypeOptions | MISSING, Field(description='Heat pump type') + ] = MISSING hydronicReheat: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag to identify whether the HVAC has a hydronic reheat system, (deprecated, set to false)' ), - ] = None + ] = MISSING isHeatingSysWeatherized: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag to identify whether the heating system is weatherized, (deprecated, set to false)' ), - ] = None + ] = MISSING perimeterSystem: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag to identify whether the HVAC system is used to condition perimeter zones' ), - ] = None + ] = MISSING propCoolingEquipEfficiencyPartial: Annotated[ - float | None, + float | None | MISSING, Field( description='Proposed system cooling equipment part load efficiency', ge=0.0 ), - ] = None + ] = MISSING propCoolingEquipEfficiency: Annotated[ - float | None, + float | None | MISSING, Field(description='Proposed system cooling equipment efficiency', ge=0.0), - ] = None + ] = MISSING propHeatingEquipEfficiency: Annotated[ - float | None, + float | None | MISSING, Field(description='Proposed system heating equipment efficiency', ge=0.0), - ] = None + ] = MISSING quantity: Annotated[ - int | None, Field(description='Quantity of HVAC system', ge=1) - ] = None + int | None | MISSING, Field(description='Quantity of HVAC system', ge=0) + ] = MISSING quantityCoolEquip: Annotated[ - int | None, Field(description='Quantity of the cooling equipment', ge=0) - ] = None + int | None | MISSING, + Field(description='Quantity of the cooling equipment', ge=0), + ] = MISSING quantityHeatEquip: Annotated[ - int | None, Field(description='Quantity of the heating equipment', ge=0) - ] = None + int | None | MISSING, + Field(description='Quantity of the heating equipment', ge=0), + ] = MISSING reheatRecoolCoil: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag to identify whether the coils are reheat and recooled (deprecated, set to false)' ), - ] = None + ] = MISSING + requirementAnswer: Annotated[ + list[Requirements] | None, Field(validate_default=True) + ] = [] returnFanHp: Annotated[ - float | None, Field(description='Return fan HP (deprecated, set to 0)', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Return fan HP (deprecated, set to 0)', ge=0.0), + ] = MISSING steamReheat: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag to identify whether the HVAC system is steam reheated (deprecated, set to false)' ), - ] = None + ] = MISSING supplyFanHp: Annotated[ - float | None, Field(description='Supply fan HP (deprecated, set to 0)', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Supply fan HP (deprecated, set to 0)', ge=0.0), + ] = MISSING supplyStaticPressure: Annotated[ - float | None, + float | None | MISSING, Field(description='Supply static pressure (deprecated, set to 0)', ge=0.0), - ] = None + ] = MISSING systemType: Annotated[ - str | None, Field(description='System type (deprecated, set to HVAC)') - ] = None + str | None | MISSING, Field(description='System type (deprecated, set to HVAC)') + ] = MISSING totalFanHp: Annotated[ - float | None, Field(description='Total fan HP (deprecated, set to 0)', ge=0.0) - ] = None - varAirBox: Annotated[bool | None, Field(description='VAR Air Box')] = None - varAirVolMixingBox: Annotated[ - bool | None, Field(description='VAR air volume mixing box') - ] = None - zone: Annotated[ZoneLayoutOptions | None, Field(description='HVAC zone layout')] = ( - None + float | None | MISSING, + Field(description='Total fan HP (deprecated, set to 0)', ge=0.0), + ] = MISSING + varAirBox: Annotated[bool | None | MISSING, Field(description='VAR Air Box')] = ( + MISSING ) + varAirVolMixingBox: Annotated[ + bool | None | MISSING, Field(description='VAR air volume mixing box') + ] = MISSING + zone: Annotated[ + ZoneLayoutOptions | MISSING, Field(description='HVAC zone layout') + ] = MISSING class FixtureSchedule(CustomBaseModel): @@ -2657,17 +2892,17 @@ class FixtureSchedule(CustomBaseModel): extra='ignore', ) id: Annotated[ - int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING lightingId: Annotated[ - int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of the parent lighting group.' ), - ] = None + ] = MISSING scheduleFixtureKey: Annotated[ str, Field(description='UUID to identify this fixture schedule.') ] @@ -2684,32 +2919,35 @@ class FixtureSchedule(CustomBaseModel): float | None, Field(description='fixture wattage', ge=0.0) ] lightingType: Annotated[ - LightingTypeOptions, Field(description='lighting fixture type') - ] + LightingTypeOptions | MISSING, Field(description='lighting fixture type') + ] = MISSING trackCircuitBreakerAmps: Annotated[ - float | None, Field(description='Track lighting circuit breaker amps', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Track lighting circuit breaker amps', ge=0.0), + ] = MISSING trackCircuitBreakerVolts: Annotated[ - float | None, + float | None | MISSING, Field(description='Track lighting circuit breaker voltage', ge=0.0), - ] = None + ] = MISSING trackCurrentLimiterWattage: Annotated[ - float | None, Field(description='Track current limiter wattage', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Track current limiter wattage', ge=0.0), + ] = MISSING trackLength: Annotated[ - float | None, Field(description='Track lighting length', ge=0.0) - ] = None + float | None | MISSING, Field(description='Track lighting length', ge=0.0) + ] = MISSING trackTotalLuminaireWattage: Annotated[ - float | None, + float | None | MISSING, Field(description='Track lighting total luminaire wattage', ge=0.0), - ] = None + ] = MISSING trackTransformerWattage: Annotated[ - float | None, Field(description='Track lighting former wattage', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Track lighting former wattage', ge=0.0), + ] = MISSING trackLightingWattageBasisType: Annotated[ - TrackLightingWattageBasisTypeOptions | None, + TrackLightingWattageBasisTypeOptions | MISSING, Field(description='Track lighting wattage basis type'), - ] = None + ] = MISSING class HVACPlant(CustomBaseModel): @@ -2717,136 +2955,145 @@ class HVACPlant(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING boilerDraftType: Annotated[ - BoilerDraftTypeOptions | None, Field(description='Boiler draft type') - ] = None + BoilerDraftTypeOptions | MISSING, Field(description='Boiler draft type') + ] = MISSING boilerFuel: Annotated[ - FuelTypeOptions | None, Field(description='Boiler fuel type') - ] = None - chiller: Annotated[ChillerTypeOptions | None, Field(description='Chiller type')] = ( - None - ) + FuelTypeOptions | MISSING, Field(description='Boiler fuel type') + ] = MISSING + chiller: Annotated[ + ChillerTypeOptions | MISSING, Field(description='Chiller type') + ] = MISSING condenser: Annotated[ - CondenserTypeOptions | None, Field(description='Condenser type') - ] = None + CondenserTypeOptions | MISSING, Field(description='Condenser type') + ] = MISSING condenserFlowRate: Annotated[ - float | None, + float | None | MISSING, Field( description='Condenser flow rate - this data comes from engine. User change this data does not have impact on compliance check', ge=0.0, ), - ] = None + ] = MISSING condenserLeavingTemperature: Annotated[ - float | None, + float | None | MISSING, Field( description='leaving water temperature - this data comes from engine. User change this data does not have impact on compliance check', ge=0.0, ), - ] = None + ] = MISSING coolingPlant: Annotated[ - CoolingPlantTypeOptions | None, Field(description='Cooling Plant Types') - ] = None + CoolingPlantTypeOptions | MISSING, Field(description='Cooling Plant Types') + ] = MISSING coolingPlantCapacity: Annotated[ - float | None, Field(description='Cooling plant capacity', ge=0.0) - ] = None - description: Annotated[str | None, Field(description='Plant unique name')] = None + float | None | MISSING, Field(description='Cooling plant capacity', ge=0.0) + ] = MISSING + description: Annotated[ + str | None | MISSING, Field(description='Plant unique name') + ] = MISSING enteringCondenserWaterTemperature: Annotated[ - float | None, + float | None | MISSING, Field( description='Condenser entering water temperature - this data comes from engine. User change this data does not have impact on compliance check', ge=0.0, ), - ] = None + ] = MISSING evaporatorLeavingTemperature: Annotated[ - float | None, + float | None | MISSING, Field( description='Evaporator leaving water temperature - this data comes from engine. User change this data does not have impact on compliance check', ge=0.0, ), - ] = None + ] = MISSING heatingPlant: Annotated[ - HeatingPlantTypeOptions | None, Field(description='Heating Plant Types') - ] = None + HeatingPlantTypeOptions | MISSING, Field(description='Heating Plant Types') + ] = MISSING heatingPlantCapacity: Annotated[ - float | None, Field(description='Heating Plant Capacity', ge=0.0) - ] = None + float | None | MISSING, Field(description='Heating Plant Capacity', ge=0.0) + ] = MISSING heatPumpChillerHeatingSourceConditions: Annotated[ - HeatPumpChillerHeatingSourceConditionOptions | None, + HeatPumpChillerHeatingSourceConditionOptions | MISSING, Field(description='Heat Pump Chiller Heating Source Condition'), - ] = None + ] = MISSING heatPumpChillerLeavingHeatingWaterTemp: Annotated[ - HeatPumpChillerLeavingHeatingWaterTempOptions | None, + HeatPumpChillerLeavingHeatingWaterTempOptions | MISSING, Field(description='Heat pump chiller leaving heating water temperature'), - ] = None + ] = MISSING heatPumpChillerType: Annotated[ - HeatPumpChillerTypeOptions | None, Field(description='Heat pump chiller type') - ] = None + HeatPumpChillerTypeOptions | MISSING, + Field(description='Heat pump chiller type'), + ] = MISSING heatRecovery: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag indicates whether the system has heat recovery feature' ), - ] = None + ] = MISSING heatPumpSimultaneousCoolingAndHeating: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag indicates whether the heat pump can do simultaneous cooling and heating' ), - ] = None + ] = MISSING heatRejection: Annotated[ - HeatRejectionTypeOptions | None, Field(description='Heat rejection types') - ] = None + HeatRejectionTypeOptions | MISSING, Field(description='Heat rejection types') + ] = MISSING leavingChilledWaterTemperature: Annotated[ - float | None, + float | None | MISSING, Field( description='Leaving chiller water temperature - this data comes from engine. User change this data does not have impact on compliance check', ge=0.0, ), - ] = None - plantType: Annotated[PlantTypeOptions | None, Field(description='Plant type')] = ( - None - ) + ] = MISSING + plantType: Annotated[ + PlantTypeOptions | MISSING, Field(description='Plant type') + ] = MISSING propCoolingPlantEfficiencyPartial: Annotated[ - float | None, + float | None | MISSING, Field(description='Proposed cooling plant part load efficiency', ge=0.0), - ] = None + ] = MISSING propCoolingPlantEfficiency: Annotated[ - float | None, Field(description='Proposed cooling plant efficiency', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Proposed cooling plant efficiency', ge=0.0), + ] = MISSING propHeatingPlantEfficiency: Annotated[ - float | None, Field(description='Proposed heating plant efficiency', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Proposed heating plant efficiency', ge=0.0), + ] = MISSING quantity: Annotated[ - int | None, Field(description='Quantity of the plant system', ge=1) - ] = None - systemType: Annotated[str | None, Field(description='Deprecated, system type')] = ( - None - ) + int | MISSING, Field(description='Quantity of the plant system', ge=0) + ] = MISSING + systemType: Annotated[ + str | None | MISSING, Field(description='Deprecated, system type') + ] = MISSING twoPipeSystem: Annotated[ - bool | None, + bool | None | MISSING, Field(description='Flag identifies if the plant system is a two pipe system'), - ] = None + ] = MISSING waterloopHeatPump: Annotated[ - bool | None, + bool | None | MISSING, Field( description='Flag identifies if the plant system is a water loop heat pump' ), - ] = None + ] = MISSING compliancePath: Annotated[ - CompliancePathOptions | None, Field(description='Compliance path') - ] = None + CompliancePathOptions | None | MISSING, Field(description='Compliance path') + ] = MISSING efficiencyRequirementException: Annotated[ - EquipmentEfficiencyRequirementExceptionOptions | None, + EquipmentEfficiencyRequirementExceptionOptions | MISSING, Field(description='Natural gas boiler efficiency requirement exceptions'), - ] = 'EFF_EXCEPTION_UNSPECIFIED' + ] = MISSING + requirementAnswer: Annotated[ + list[Requirements] | None, Field(validate_default=True) + ] = [] class Fan(CustomBaseModel): @@ -2854,55 +3101,60 @@ class Fan(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING brakeHp: Annotated[ - float | None, + float | MISSING, Field( description='Maximum brake HP, this value is calculated by engine.', ge=0.0 ), - ] = None - description: Annotated[str | None, Field(description='Unique name of the fan')] = ( - None - ) + ] = MISSING + description: Annotated[ + str | None | MISSING, Field(description='Unique name of the fan') + ] = MISSING designBrakeHp: Annotated[ - float | None, Field(description='Fan design brake HP', ge=0.0) - ] = None + float | MISSING, Field(description='Fan design brake HP', ge=0.0) + ] = MISSING fanDesignEfficiency: Annotated[ - float | None, Field(description='Fan design efficiency', ge=0.0, le=100.0) - ] = None + float | None | MISSING, + Field(description='Fan design efficiency', ge=0.0, le=100.0), + ] = MISSING fanEfficiency: Annotated[ - float | None, + float | MISSING, Field( description='Fan efficiency. In latest code, it is the FEI, legacy code (IECC 2015 e.g.,) it is FEG. Based on what index it is, the range is different.', ge=0.0, ), - ] = None + ] = MISSING fanEfficiencyExceptionType: Annotated[ - FanEfficiencyExceptionTypeOptions | None, + FanEfficiencyExceptionTypeOptions | MISSING, Field(description='Fan efficiency execption type'), - ] = None - fanType: Annotated[FanTypeOptions | None, Field(description='Fan type')] = None + ] = MISSING + fanType: Annotated[FanTypeOptions | MISSING, Field(description='Fan type')] = ( + MISSING + ) fanVolumeType: Annotated[ - FanVolumeTypeOptions | None, Field(description='Fan volume control type') - ] = None + FanVolumeTypeOptions | MISSING, Field(description='Fan volume control type') + ] = MISSING maxNameplateHp: Annotated[ - float | None, + float | None | MISSING, Field( - description='Maximum name plate HP - this number shall be calculated by engine', - ge=0.0, + description='Maximum name plate HP - this number shall be calculated by engine' ), - ] = None - nameplateHp: Annotated[float | None, Field(description='Name plate HP', ge=0.0)] = ( - None + ] = MISSING + nameplateHp: Annotated[float | MISSING, Field(description='Name plate HP')] = ( + MISSING ) totalFanEfficiency: Annotated[ - float | None, Field(description='Peak fan efficiency', ge=0.0, le=100.0) - ] = None - volume: Annotated[float | None, Field(description='Fan volume', ge=0.0)] = None + float | None | MISSING, + Field(description='Peak fan efficiency', ge=0.0, le=100.0), + ] = MISSING + volume: Annotated[float | MISSING, Field(description='Fan volume', ge=0.0)] = ( + MISSING + ) class FanSystemPressureDropCredits(CustomBaseModel): @@ -2910,38 +3162,39 @@ class FanSystemPressureDropCredits(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING cfm: Annotated[ - float | None, + float | MISSING, Field( description='Effective air volume when applying pressure drop credit', ge=0.0, ), - ] = None + ] = MISSING designCredit: Annotated[ - float | None, + float | MISSING, Field( description='value for pressure drop credit at design conditions or apply to clean filter pressure drop', ge=0.0, ), - ] = None + ] = MISSING pressureDropCredit: Annotated[ - float | None, + float | MISSING, Field( description='The calculated pressure drop credits. This is calculated value from engine.', ge=0.0, ), - ] = None + ] = MISSING recoveryEffectiveness: Annotated[ - float | None, Field(description='Energy recovery effectiveness', ge=0.0, le=1.0) - ] = None + float | None | MISSING, + Field(description='Energy recovery effectiveness', ge=0.0), + ] = MISSING verticalDuctLength: Annotated[ - float | None, Field(description='Vertical duct length', ge=0.0) - ] = None + float | None | MISSING, Field(description='Vertical duct length', ge=0.0) + ] = MISSING type: Annotated[PressureDropTypeOptions, Field(description='Pressure drop type')] @@ -2950,86 +3203,88 @@ class ServiceWaterHeatingSystem(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING circulationPump: Annotated[ bool | None, Field(description='Flag identifies whether the SWH has a circulation pump'), - ] = None + ] = False heatTraceTapeInstalled: Annotated[ bool | None, Field( description='Flag identifies whether the SWH has heat trace tape installed' ), - ] = None + ] = False combinedSystem: Annotated[ bool | None, Field(description='Flag identifies whether the SWH is a combined system'), - ] = None + ] = False poolSystem: Annotated[ bool | None, Field(description='Flag identifies whether the SWH is part of pool system'), - ] = None - heatpumpPoolHeater: Annotated[ - bool | None, + ] = False + heatPumpPoolHeater: Annotated[ + HeatPumpPoolHeater | None, Field( description='Flag identifies whether the SWH uses heat pump to heat the pool. - Only used when poolSystem is true. False as default' ), ] = None inputRating: Annotated[ - float | None, + float | MISSING, Field( description='Water heater rated input power. kBtu/h if fuel type is gas or oil, kW if fuel type is electric', ge=0.0, ), - ] = None + ] = MISSING swhSystemDrawPatternType: Annotated[ - SWHSystemDrawPatternTypeOptions | None, + SWHSystemDrawPatternTypeOptions | MISSING, Field(description='SWH system draw pattern type'), - ] = None + ] = MISSING storageCapacity: Annotated[ - float | None, Field(description='Water heater storage capacity', ge=0.0) - ] = None + float | MISSING, Field(description='Water heater storage capacity', ge=0.0) + ] = MISSING propSwhEquipEfficiency: Annotated[ - float | None, + float | MISSING, Field( description='Proposed SWH equipment efficiency. Standby Loss if fuel type is electric, %Et otherwise.', ge=0.0, ), - ] = None + ] = MISSING quantity: Annotated[ - int | None, Field(description='Quantity of the water heater', ge=1) - ] = None + int | MISSING, Field(description='Quantity of the water heater', ge=0) + ] = MISSING description: Annotated[ - str | None, Field(description='Unique name describes the SWH system') - ] = None + str | None | MISSING, Field(description='Unique name describes the SWH system') + ] = MISSING systemType: Annotated[ - str | None, Field(description='Deprecated - set to Water Heater') - ] = None + str | MISSING, Field(description='Deprecated - set to Water Heater') + ] = MISSING swhSystemType: Annotated[ - SWHSystemTypeOptions | None, Field(description='SWH system type.') - ] = None + SWHSystemTypeOptions | MISSING, Field(description='SWH system type.') + ] = MISSING fuelType: Annotated[ - SWHFuelTypeOptions | None, Field(description='SWH equipment fuel type') - ] = None + SWHFuelTypeOptions | MISSING, Field(description='SWH equipment fuel type') + ] = MISSING altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='SWH exemptions') - ] = None + AltExemptTypeOptions | None | MISSING, Field(description='SWH exemptions') + ] = MISSING swhSystemSubType: Annotated[ - SWHSystemSubTypeOptions | None, + SWHSystemSubTypeOptions | MISSING, Field(description='Electric storage water heater sub types'), - ] = 'UNKNOWN_SWH_SYSTEM_SUB_TYPE' - listPosition: int | None = None + ] = MISSING + listPosition: int | None | MISSING = MISSING efficiencyRequirementException: Annotated[ - EquipmentEfficiencyRequirementExceptionOptions | None, + EquipmentEfficiencyRequirementExceptionOptions | MISSING, Field( description='High input natural gas service water heater efficiency requirement exceptions' ), - ] = 'EFF_EXCEPTION_UNSPECIFIED' - requirementAnswer: list[Any] | None = None + ] = MISSING + requirementAnswer: Annotated[ + list[Requirements] | None, Field(validate_default=True) + ] = [] class EfficiencyPackage(CustomBaseModel): @@ -3037,139 +3292,141 @@ class EfficiencyPackage(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING bldgUseKey: Annotated[ - str | None, Field(description='Building use Key, reference BuildingAreaUse key') - ] = None + str | None | MISSING, + Field(description='Building use Key, reference BuildingAreaUse key'), + ] = MISSING airInfiltration: Annotated[ - float | None, Field(description='Air infiltration measured @75Pa', ge=0.0) - ] = None - kitchenSize: Annotated[float | None, Field(description='Kitchen size', ge=0.0)] = ( - None - ) + float | None | MISSING, + Field(description='Air infiltration measured @75Pa', ge=0.0), + ] = MISSING + kitchenSize: Annotated[ + float | None | MISSING, Field(description='Kitchen size', ge=0.0) + ] = MISSING renewableCapacity: Annotated[ - float | None, + float | None | MISSING, Field( description='On-site renewable capacity, unit is Btu for thermal system and watts for electric system.', ge=0.0, ), - ] = None + ] = MISSING renewableType: Annotated[ - EnergyCreditRenewableTypeOptions | None, + EnergyCreditRenewableTypeOptions | None | MISSING, Field(description='Renewable system type'), - ] = None + ] = MISSING fractionGrossFloorAreaServedByGSHP: Annotated[ - float | None, + float | None | MISSING, Field( description='Fraction of gross floor area served by GSHP, Added by 90.1 2022', ge=0.0, le=1.0, ), - ] = None + ] = MISSING gshpFieldSourceCapacityType: Annotated[ - GSHPFieldSourceCapacityTypeOptions | None, + GSHPFieldSourceCapacityTypeOptions | None | MISSING, Field(description='GSHP field source capacity type, Added by 90.1 2022'), - ] = None + ] = MISSING fractionGrossFloorAreaServedByCAV: Annotated[ - float | None, + float | None | MISSING, Field( description='Fraction of gross floor area served by CAV, Added by 90.1 2022', ge=0.0, le=1.0, ), - ] = None + ] = MISSING coolingEnergyRecoveryRatio: Annotated[ - float | None, + float | None | MISSING, Field( description='Cooling energy recovery ratio, Added by 90.1 2022', ge=0.0, le=1.0, ), - ] = None + ] = MISSING heatingEnergyRecoveryRatio: Annotated[ - float | None, + float | None | MISSING, Field( description='Heating energy recovery ratio, Added by 90.1 2022', ge=0.0, le=1.0, ), - ] = None + ] = MISSING percentageWaterPipingWithIncreasedInsulation: Annotated[ - float | None, + float | None | MISSING, Field( description='Percentage water piping with increased insulation, Added by 90.1 2022', ge=0.0, le=100.0, ), - ] = None + ] = MISSING numberShowersWithDrainHeatRecovery: Annotated[ - int | None, + int | None | MISSING, Field( description='Number of showers with drain heat recovery, Added by 90.1 2022', ge=1, ), - ] = None + ] = MISSING totalNumberShowers: Annotated[ - int | None, + int | None | MISSING, Field(description='Total number of showers, Added by 90.1 2022', ge=1), - ] = None + ] = MISSING fractionTunedAreaOfGrossLightedFloorArea: Annotated[ - float | None, + float | None | MISSING, Field( description='Fraction of tuned area of gross lighted floor area, Added by 90.1 2022', ge=0.0, le=1.0, ), - ] = None + ] = MISSING grossLightedFloorArea: Annotated[ - float | None, + float | None | MISSING, Field(description='Gross lighted floor area, Added by 90.1 2022', ge=0.0), - ] = None + ] = MISSING actualDaylightAreaWithContinuousDim: Annotated[ - float | None, + float | None | MISSING, Field( description='Actual daylight area with continous dimming control, Added by 90.1 2022', ge=0.0, ), - ] = None + ] = MISSING percentageLightingLoadManagement: Annotated[ - float | None, + float | None | MISSING, Field( description='Percentage lighting load management, Added by 90.1 2022', ge=0.0, le=1.0, ), - ] = None + ] = MISSING installedElectricStorageCapacity: Annotated[ - float | None, + float | None | MISSING, Field( description='Installed electric storage capacity, Added by 90.1 2022', ge=0.0, ), - ] = None + ] = MISSING storageRatio: Annotated[ - float | None, + float | None | MISSING, Field( description='HVAC cooling energy storage ratio, unit in ton-hours storage per ton of design-day cooling load , Added by 90.1 2022', ge=0.5, le=4.0, ), - ] = None + ] = MISSING sumOfFloorsServedByClassAElevators: Annotated[ - int | None, + int | None | MISSING, Field(description='Sum of floors served by each Class A elevators', ge=0), - ] = None + ] = MISSING sumOfFloorsServedByClassBElevators: Annotated[ - int | None, + int | None | MISSING, Field( description='sum of floors served by all building elevators and escalators', ge=0, ), - ] = None + ] = MISSING type: Annotated[ EfficiencyPackageTypeOptions | MAS2022EnergyCreditTypeOptions @@ -3184,11 +3441,11 @@ class RenewableSystem(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING description: Annotated[ str | None, Field(description='description of the renewable system') ] @@ -3209,11 +3466,11 @@ class OffsiteRenewableProcurement(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING description: Annotated[ str | None, Field(description='description of the offsite renewable procurement'), @@ -3235,14 +3492,15 @@ class LightingControls(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING type: Annotated[ - LightingControlTypeOptions | None, Field(description='Lighting Control types') - ] = None + LightingControlTypeOptions | MISSING, + Field(description='Lighting Control types'), + ] = MISSING class ThermalBridge(CustomBaseModel): @@ -3250,28 +3508,36 @@ class ThermalBridge(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING thermalBridgeType: Annotated[ - ThermalBridgeTypeOptions | None, Field(description='thermal bridge type') - ] = None + ThermalBridgeTypeOptions | None | MISSING, + Field(description='thermal bridge type'), + ] = MISSING thermalBridgeCategory: Annotated[ - ThermalBridgeCategoryOptions | None, + ThermalBridgeCategoryOptions | None | MISSING, Field(description='thermal bridge category'), - ] = None + ] = MISSING thermalBridgeComplianceType: Annotated[ - ThermalBridgeComplianceTypeOptions | None, + ThermalBridgeComplianceTypeOptions | MISSING, Field(description='thermal bridge compliance type'), - ] = None - psiFactor: Annotated[float | None, Field(description='Psi factor')] = None + ] = MISSING + psiFactor: Annotated[float | None | MISSING, Field(description='Psi factor')] = ( + MISSING + ) thermalBridgeLength: Annotated[ - float | None, Field(description='linear length of a thermal bridge - ft') - ] = None - chiFactor: Annotated[float | None, Field(description='Chi factor')] = None - numberOfPoints: Annotated[int | None, Field(description='Number of points')] = None + float | None | MISSING, + Field(description='linear length of a thermal bridge - ft'), + ] = MISSING + chiFactor: Annotated[float | None | MISSING, Field(description='Chi factor')] = ( + MISSING + ) + numberOfPoints: Annotated[ + int | None | MISSING, Field(description='Number of points') + ] = MISSING class AgWall(CustomBaseModel): @@ -3279,31 +3545,30 @@ class AgWall(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None - description: Annotated[ - str | None, Field(description='The name of the component') - ] = '' + ] = MISSING + description: Annotated[str | None, Field(description='The name of the component')] assemblyType: Annotated[ str | None, Field(description='The type of the component') ] = 'Exterior Wall' bldgUseKey: Annotated[ - str, Field(description='key reference of the building use area data group') + str | None, + Field(description='key reference of the building use area data group'), ] wallType: Annotated[ - WallTypeOptions | None, Field(description='Above grade wall type') - ] = None + WallTypeOptions | MISSING, Field(description='Above grade wall type') + ] = MISSING agWallConstructionDetailsType: Annotated[ - AgWallConstructionDetailsTypeOptions | None, + AgWallConstructionDetailsTypeOptions, Field(description='Above grade wall construction details type'), - ] = 'NONE' + ] agWallExteriorFinishDetailsType: Annotated[ AgWallExteriorFinishDetailsTypeOptions | None, Field(description='Above grade wall exterior finish details'), - ] = None + ] nextToUncondSpace: Annotated[ bool | None, Field( @@ -3315,56 +3580,56 @@ class AgWall(CustomBaseModel): Field( description='Not sure why basement has this data, likely deprecated. Use null' ), - ] = None + ] otherWallType: Annotated[ AgWallOtherTypeOptions | None, Field(description='other wall types') - ] = 'NONE' + ] adjacentSpaceType: Annotated[ AdjacentSpaceTypeOptions | None, Field(description='Space type of the adjacent space'), - ] = None + ] adjacentSpaceBuildingType: Annotated[ - WholeBuildingTypeOptions | None, + WholeBuildingTypeOptions | MISSING, Field(description='Building type of the adjacent space'), - ] = None + ] = MISSING thermalBridge: Annotated[ list[ThermalBridge], Field(description='Thermal bridges objects', min_length=0) ] thermalBridgeExceptionType: Annotated[ ThermalBridgeExceptionTypeOptions | None, Field(description='Type of thermal bridge exceptions'), - ] = None + ] effectiveUFactor: Annotated[ float | None, Field(description='The effective U factor after thermal bridge adjustment'), - ] = None + ] thermalBridgeAdjustmentFactor: Annotated[ float | None, Field(description='Thermal bridge adjustment factor') ] = None allowanceType: Annotated[ - EnvelopeAssemblyAllowanceTypeOptions | None, Field(description='allowance type') - ] = None - cmuType: Annotated[CMUTypeOptions | None, Field(description='CMU type')] = None + EnvelopeAssemblyAllowanceTypeOptions, Field(description='allowance type') + ] + cmuType: Annotated[CMUTypeOptions | None, Field(description='CMU type')] concreteDensity: Annotated[ - ConcreteDensityOptions | None, Field(description='Concrete density') - ] = 0 + ConcreteDensityOptions, Field(description='Concrete density') + ] concreteThickness: Annotated[ - ConcreteThicknessOptions | None, Field(description='Concrete thickness') - ] = 0 + ConcreteThicknessOptions, Field(description='Concrete thickness') + ] constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None - exemptionType: EnvelopeAssemblyExemptionOptions | None = None - furringType: FurringTypeOptions | None = None + ] = MISSING + exemptionType: EnvelopeAssemblyExemptionOptions | None + furringType: FurringTypeOptions | None heatCapacity: Annotated[ float | None, Field( description='heat capacity of a mass wall. Used in other mass wall type', ge=0.0, ), - ] = 0.0 - orientation: OrientationOptions | None = 'UNSPECIFIED_ORIENTATION' + ] + orientation: OrientationOptions window: Annotated[ list[Window], Field(description='Windows on the wall', min_length=0) ] @@ -3372,35 +3637,30 @@ class AgWall(CustomBaseModel): cavityRValue: Annotated[ float | None, Field( - description='Average insulation R-value in the cavity between two studs.', - ge=0.0, + description='Average insulation R-value in the cavity between two studs.' ), - ] = 0.0 + ] continuousRValue: Annotated[ float | None, Field( - description='Continuous insulation on the above grade wall. Can be exterior or interior or both.', - ge=0.0, + description='Continuous insulation on the above grade wall. Can be exterior or interior or both.' ), - ] = 0.0 + ] continuousDeratedRValue: Annotated[ - float | None, + float | None | MISSING, Field( description='Continuous R value derated factor for thermal bridge effect', ge=0.0, ), - ] = 0.0 + ] = MISSING propUValue: Annotated[ float | None, - Field( - description='Proposed thermal transmittance of the above grade wall.', - ge=0.0, - ), - ] = 0.0 + Field(description='Proposed thermal transmittance of the above grade wall.'), + ] altExemptType: Annotated[ AltExemptTypeOptions | None, Field(description='alteration exemption type') ] = None - grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] = 0.0 + grossArea: Annotated[float | None, Field(description='gross area')] class BgWall(CustomBaseModel): @@ -3408,67 +3668,62 @@ class BgWall(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None - description: Annotated[ - str | None, Field(description='The name of the component') - ] = '' - assemblyType: Annotated[ - str | None, Field(description='The type of the component') - ] = 'Basement' + ] = MISSING + description: Annotated[str | None, Field(description='The name of the component')] + assemblyType: Annotated[str, Field(description='The type of the component')] bldgUseKey: Annotated[ - str, Field(description='key reference of the building use area data group') + str | None, + Field(description='key reference of the building use area data group'), ] wallType: Annotated[ - BgWallTypeOptions | None, Field(description='Below grade wall type') - ] = None + BgWallTypeOptions | MISSING, Field(description='Below grade wall type') + ] = MISSING wallHeight: Annotated[ - float | None, Field(description='Total height of a below grade wall') - ] = 0.0 - wallHeightBelowGrade: Annotated[ - float | None, Field(description='Wall height below grade') - ] = 0.0 + float, Field(description='Total height of a below grade wall') + ] + wallHeightBelowGrade: Annotated[float, Field(description='Wall height below grade')] adjacentSpaceType: Annotated[ AdjacentSpaceTypeOptions | None, Field(description='Space type of the adjacent space'), - ] = None + ] adjacentSpaceBuildingType: Annotated[ - WholeBuildingTypeOptions | None, + WholeBuildingTypeOptions | MISSING, Field(description='Building type of the adjacent space'), - ] = None + ] = MISSING allowanceType: Annotated[ - EnvelopeAssemblyAllowanceTypeOptions | None, Field(description='allowance type') - ] = None - cmuType: Annotated[CMUTypeOptions | None, Field(description='CMU type')] = None + EnvelopeAssemblyAllowanceTypeOptions, Field(description='allowance type') + ] + cmuType: Annotated[CMUTypeOptions | None, Field(description='CMU type')] concreteDensity: Annotated[ - ConcreteDensityOptions | None, Field(description='Concrete density') - ] = 0 + ConcreteDensityOptions, Field(description='Concrete density') + ] concreteThickness: Annotated[ - ConcreteThicknessOptions | None, Field(description='Concrete thickness') - ] = 0 + ConcreteThicknessOptions, Field(description='Concrete thickness') + ] constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None - exemptionType: EnvelopeAssemblyExemptionOptions | None = None - furringType: FurringTypeOptions | None = None + ] = MISSING + exemptionType: EnvelopeAssemblyExemptionOptions | None + furringType: FurringTypeOptions | None heatCapacity: Annotated[ float | None, Field( description='heat capacity of a mass wall. Used in other mass wall type', ge=0.0, ), - ] = 0.0 - orientation: OrientationOptions | None = 'UNSPECIFIED_ORIENTATION' + ] + orientation: OrientationOptions insulationPosition: Annotated[ str | None, Field( description='Not sure why basement has this data, likely deprecated. Use null' ), - ] = None + ] window: Annotated[ list[Window], Field(description='Windows on the wall', min_length=0) ] @@ -3476,28 +3731,23 @@ class BgWall(CustomBaseModel): cavityRValue: Annotated[ float | None, Field( - description='Average insulation R-value in the cavity between two studs.', - ge=0.0, + description='Average insulation R-value in the cavity between two studs.' ), - ] = 0.0 + ] continuousRValue: Annotated[ float | None, Field( - description='Continuous insulation on the below grade wall. Can be exterior or interior or both.', - ge=0.0, + description='Continuous insulation on the below grade wall. Can be exterior or interior or both.' ), ] = 0.0 propUValue: Annotated[ float | None, - Field( - description='Proposed thermal transmittance of the below grade wall.', - ge=0.0, - ), - ] = 0.0 + Field(description='Proposed thermal transmittance of the below grade wall.'), + ] altExemptType: Annotated[ AltExemptTypeOptions | None, Field(description='alteration exemption type') ] = None - grossArea: Annotated[float | None, Field(description='gross area', ge=0.0)] = 0.0 + grossArea: Annotated[float, Field(description='gross area', ge=0.0)] class Fixture(CustomBaseModel): @@ -3505,79 +3755,94 @@ class Fixture(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING advControlsAllowanceAperture: Annotated[ - float | None, Field(description='Advanced controls allowance aperture', ge=0.0) - ] = None - advControlAllowanceType: Annotated[ + float | None | MISSING, + Field(description='Advanced controls allowance aperture', ge=0.0), + ] = MISSING + advControlsAllowanceType: Annotated[ AdvancedControlsAllowanceTypeOptions | None, - Field(description='Advanced control allowance type'), + Field(description='Advanced controls allowance type'), ] = None allowanceFloorArea: Annotated[ - float | None, Field(description='Floor area covered by the allowance', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Floor area covered by the allowance', ge=0.0), + ] = MISSING allowanceType: Annotated[ - LightingAllowanceTypeOptions | None, + LightingAllowanceTypeOptions | MISSING, Field(description='Lighting allowance type'), - ] = None - ballast: Annotated[BallastTypeOptions | None, Field(description='Ballast type')] = ( - None - ) + ] = MISSING + ballast: Annotated[ + BallastTypeOptions | MISSING, Field(description='Ballast type') + ] = MISSING description: Annotated[str | None, Field(description='Description of the fixture')] exemptionType: Annotated[ - LightingExemptionTypeOptions | None, + LightingExemptionTypeOptions | None | MISSING, Field(description='lighting exemption type'), - ] = None + ] = MISSING fixtureType: Annotated[ str | None, Field(description='This field temporarily used to describe the fixture.'), - ] = None + ] fixtureWattage: Annotated[ - float | None, Field(description='fixture wattage', ge=0.0) - ] = None - lampType: Annotated[str | None, Field(description='deprecated, use null')] = None + float | None | MISSING, Field(description='fixture wattage', ge=0.0) + ] = MISSING + lampType: Annotated[ + str | None | MISSING, Field(description='deprecated, use null') + ] = MISSING lightingType: Annotated[ - LightingTypeOptions, Field(description='lighting fixture type') - ] + LightingTypeOptions | MISSING, Field(description='lighting fixture type') + ] = MISSING numberOfLamps: Annotated[ - float | None, Field(description='deprecated, use null') - ] = None + float | None | MISSING, Field(description='deprecated, use null') + ] = MISSING powerAllowance: Annotated[ - float | None, Field(description='Advanced control power allowance', ge=0.0) - ] = None - quantity: Annotated[int, Field(description='Quantity of the fixture', ge=0)] + float | None | MISSING, + Field(description='Advanced control power allowance', ge=0.0), + ] = MISSING + quantity: Annotated[int | None, Field(description='Quantity of the fixture', ge=0)] quantityWithAdvControls: Annotated[ - int | None, + int | None | MISSING, Field(description='Quantity of the fixture that has advanced control', ge=0), - ] = None + ] = MISSING + scheduleFixtureKey: Annotated[ + str | None | MISSING, + Field(description='UUID to identify this fixture schedule.'), + ] = MISSING trackCircuitBreakerAmps: Annotated[ - float | None, Field(description='Track lighting circuit breaker amps', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Track lighting circuit breaker amps', ge=0.0), + ] = MISSING trackCircuitBreakerVolts: Annotated[ - float | None, + float | None | MISSING, Field(description='Track lighting circuit breaker voltage', ge=0.0), - ] = None + ] = MISSING trackCurrentLimiterWattage: Annotated[ - float | None, Field(description='Track current limiter wattage', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Track current limiter wattage', ge=0.0), + ] = MISSING trackLength: Annotated[ - float | None, Field(description='Track lighting length', ge=0.0) - ] = None + float | None | MISSING, Field(description='Track lighting length', ge=0.0) + ] = MISSING trackTotalLuminaireWattage: Annotated[ - float | None, + float | None | MISSING, Field(description='Track lighting total luminaire wattage', ge=0.0), - ] = None + ] = MISSING trackTransformerWattage: Annotated[ - float | None, Field(description='Track lighting former wattage', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Track lighting former wattage', ge=0.0), + ] = MISSING trackLightingWattageBasisType: Annotated[ - TrackLightingWattageBasisTypeOptions | None, + TrackLightingWattageBasisTypeOptions | MISSING, Field(description='Track lighting wattage basis type'), - ] = None + ] = MISSING + typeOfFixture: Annotated[ + str | None | MISSING, Field(description='Type of the fixture') + ] = MISSING lightingControl: Annotated[ list[LightingControls], Field(description='lighting controls apply to this fixture'), @@ -3589,49 +3854,50 @@ class FanSystem(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING complianceMessage: Annotated[ - str | None, Field(description='Compliance message calculated from engine') - ] = None + str | None | MISSING, + Field(description='Compliance message calculated from engine'), + ] = MISSING complies: Annotated[ - bool | int | None, + bool | int | None | MISSING, Field(description='Flag indicate whether the fan system complies or fail'), - ] = None + ] = MISSING complyMethod: Annotated[ - FanSystemComplianceMethodOptions | None, + FanSystemComplianceMethodOptions | MISSING, Field(description='Fan system compliance method'), - ] = None + ] = MISSING description: Annotated[ - str | None, Field(description='Unique name of this fan system') - ] = None - description2: Annotated[str | None, Field(description='Number of areas served')] = ( - None - ) - fan: Annotated[list[Fan] | None, Field(description='fans')] = None + str | None | MISSING, Field(description='Unique name of this fan system') + ] = MISSING + description2: Annotated[ + str | None | MISSING, Field(description='Number of areas served') + ] = MISSING + fan: Annotated[list[Fan] | MISSING, Field(description='fans')] = MISSING hasPressureDropCredits: Annotated[ - bool | int | None, + HasPressureDropCredits | MISSING, Field(description='Flag indicates if the fan system has pressure drop credits'), - ] = None + ] = MISSING fanSystemKey: Annotated[ - str | None, + str | None | MISSING, Field( description='Fan system key, used when reference a fan system in an HVAC system' ), - ] = None + ] = MISSING pressureDropCredits: Annotated[ - list[FanSystemPressureDropCredits] | None, + list[FanSystemPressureDropCredits] | MISSING, Field(description='Pressure drop credits'), - ] = None + ] = MISSING servesAllowanceAreaWithFlowControl: Annotated[ - bool | int | None, + bool | int | None | MISSING, Field( description='Flag indicates if the fan system serves allowance area with flow control' ), - ] = None + ] = MISSING class Renewable(CustomBaseModel): @@ -3639,48 +3905,51 @@ class Renewable(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING renewableException: Annotated[ RenewableExceptionOptions, Field(description='Renewable exceptions') ] - numberOfFloors: Annotated[int, Field(description='number of floors', ge=1)] + numberOfFloors: Annotated[int, Field(description='number of floors', ge=0)] largestThreeFloorArea: Annotated[ - float, Field(description='Gross floor area of the largest three floors', ge=0.0) + float | None, + Field(description='Gross floor area of the largest three floors', ge=0.0), ] requiredCapacity: Annotated[ - float, Field(description='Code required minimum renewable capacity', ge=0.0) + float | None, + Field(description='Code required minimum renewable capacity', ge=0.0), ] proposedCapacity: Annotated[ - float, Field(description='Sum of the proposed renewable capacity', ge=0.0) + float | None, + Field(description='Sum of the proposed renewable capacity', ge=0.0), ] roofAreaForRenewable: Annotated[ - float, Field(description='roof area for renewable systems', ge=0.0) + float | None, Field(description='roof area for renewable systems', ge=0.0) ] renewableSystem: Annotated[ list[RenewableSystem], Field(description='List of renewable systems') ] requiredOffsiteRenewableEnergy: Annotated[ - float | None, + float | None | MISSING, Field( description='Sum of the required off-site renewable energy procurement', ge=0.0, ), - ] = None + ] = MISSING proposedOffsiteRenewableEnergy: Annotated[ - float | None, + float | None | MISSING, Field( description='Sum of the proposed off-site renewable energy procurement', ge=0.0, ), - ] = None + ] = MISSING offsiteRenewableProcurement: Annotated[ - list[OffsiteRenewableProcurement] | None, + list[OffsiteRenewableProcurement] | MISSING, Field(description='List of off-site renewable procurement'), - ] = None + ] = MISSING class Envelope(CustomBaseModel): @@ -3688,11 +3957,11 @@ class Envelope(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING agWall: Annotated[ list[AgWall], Field(description='above grade walls', min_length=0) ] @@ -3708,8 +3977,9 @@ class Envelope(CustomBaseModel): Field(description='Skylights, must be assigned to a roof', min_length=0), ] useOrientationDetails: Annotated[ - bool | None, Field(description='use orientation details for calculation') - ] = True + Literal[True] | MISSING, + Field(description='use orientation details for calculation'), + ] = MISSING useVltDetails: Annotated[ bool | None, Field( @@ -3720,7 +3990,9 @@ class Envelope(CustomBaseModel): bool | None, Field(description='use cool roof / high albedo roof details for calculation'), ] = True - useCoolRoofDetails: Annotated[None, Field(description='cool roof details')] = None + useCoolRoofDetails: Annotated[ + None | MISSING, Field(description='cool roof details') + ] = MISSING postAltWindowWallPct: Annotated[ float | None, Field( @@ -3771,72 +4043,79 @@ class InteriorLightingSpace(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING description: Annotated[ - str | None, Field(description='Description of the space') - ] = None + str | None | MISSING, Field(description='Description of the space') + ] = MISSING preAltNumberFixtures: Annotated[ - int | None, Field(description='Number of fixtures to be altered', ge=0) - ] = None + int | None | MISSING, + Field(description='Number of fixtures to be altered', ge=0), + ] = MISSING numFixturesAlteredOrAdded: Annotated[ - int | None, Field(description='Number of fixtures added or altered', ge=0) - ] = None + int | None | MISSING, Field(description='Number of fixtures added or altered') + ] = MISSING preAltTotalWattage: Annotated[ - float | None, Field(description='Total wattage before the alteration', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Total wattage before the alteration', ge=0.0), + ] = MISSING postAltTotalWattage: Annotated[ - float | None, Field(description='Total wattage after the alteration', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Total wattage after the alteration', ge=0.0), + ] = MISSING altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None - exemptionType: Annotated[str | None, Field(description='deprecated, use null')] = ( - None - ) - allowanceType: Annotated[str | None, Field(description='deprecated, use null')] = ( - None - ) + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING + exemptionType: Annotated[ + str | None | MISSING, Field(description='deprecated, use null') + ] = MISSING + allowanceType: Annotated[ + str | None | MISSING, Field(description='deprecated, use null') + ] = MISSING allowanceFloorArea: Annotated[ - float | None, + float | None | MISSING, Field(description='The floor area that covered by allowance', ge=0.0), - ] = None - rcrPerimeter: Annotated[float | None, Field(description='perimeter', ge=0.0)] = None + ] = MISSING + rcrPerimeter: Annotated[ + float | None | MISSING, Field(description='perimeter', ge=0.0) + ] = MISSING rcrFloorToWorkplaneHeight: Annotated[ - float | None, Field(description='Floor-to-workplane height', ge=0.0) - ] = None + float | None | MISSING, Field(description='Floor-to-workplane height', ge=0.0) + ] = MISSING rcrWorkplaneToLuminaireHeight: Annotated[ - float | None, Field(description='Workplane-to-luminaire height', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Workplane-to-luminaire height', ge=0.0), + ] = MISSING primaryDaylight: Annotated[ - float | None, + float | None | MISSING, Field(description='Daylighting on the primary sidelight area', ge=0.0), - ] = None + ] = MISSING secondaryDaylight: Annotated[ - float | None, + float | None | MISSING, Field(description='Daylighting on the secondary sidelight area', ge=0.0), - ] = None + ] = MISSING skylightToplight: Annotated[ - float | None, + float | None | MISSING, Field(description='Daylighting on the skylight toplight area', ge=0.0), - ] = None + ] = MISSING roofMonitorToplight: Annotated[ - float | None, + float | None | MISSING, Field(description='Daylighting on the roof monitor top light area', ge=0.0), - ] = None + ] = MISSING decorativeArea: Annotated[ - float | None, + float | None | MISSING, Field( description='The floor area that covered by decorative lightings', ge=0.0 ), - ] = None + ] = MISSING fixture: Annotated[ - list[Fixture] | None, + list[Fixture] | MISSING, Field(description='List of fixtures in the lighting space'), - ] = None + ] = MISSING class HVAC(CustomBaseModel): @@ -3844,17 +4123,17 @@ class HVAC(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING hvacSystem: Annotated[ list[HVACSystem], Field(description='HVAC systems - air-based or radiant-based system'), ] hvacPlant: Annotated[list[HVACPlant], Field(description='HVAC Plant - source loop')] - fanSystem: Annotated[list[FanSystem], Field(description='fan system')] + fanSystem: Annotated[list[FanSystem] | None, Field(description='Fan system')] class ActivityUse(CustomBaseModel): @@ -3862,52 +4141,55 @@ class ActivityUse(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING key: Annotated[ str, Field(description='Reference to the building use key :BuildingAreaUse.id:') ] areaDescription: Annotated[ - str | None, Field(description='Text description of the area') - ] = None + str | MISSING, Field(description='Text description of the area') + ] = MISSING floorArea: Annotated[ - float | None, Field(description='Whole building use floor area', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Whole building use floor area', ge=0.0), + ] = MISSING ceilingHeight: Annotated[ - float | None, Field(description='Average ceiling height', ge=0.0) - ] = None + float | None | MISSING, Field(description='Average ceiling height', ge=0.0) + ] = MISSING powerDensity: Annotated[ - float | None, Field(description='Internal equipment power density', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Internal equipment power density', ge=0.0), + ] = MISSING internalLoad: Annotated[ - float | None, Field(description='Internal equipment load', ge=0.0) - ] = None + float | None | MISSING, Field(description='Internal equipment load', ge=0.0) + ] = MISSING constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None + ] = MISSING allowedWattage: Annotated[ - float | None, Field(description='Allowed wattage', ge=0.0) - ] = None + float | None | MISSING, Field(description='Allowed wattage', ge=0.0) + ] = MISSING proposedWattage: Annotated[ - float | None, Field(description='Proposed wattage', ge=0.0) - ] = None + float | None | MISSING, Field(description='Proposed wattage', ge=0.0) + ] = MISSING interiorLightingSpace: Annotated[ - InteriorLightingSpace, Field(description='Interior lighting space definition') + InteriorLightingSpace | None, + Field(description='Interior lighting space definition'), ] activityType: Annotated[ - ActivityTypeOptions | None, Field(description='activity type') - ] = None + ActivityTypeOptions | MISSING, Field(description='activity type') + ] = MISSING roomCavityRatioThreshold: Annotated[ - float | None, Field(description='Room Cavity Ratio threshold') - ] = None + float | None | MISSING, Field(description='Room Cavity Ratio threshold') + ] = MISSING isUnfinishedSpace: Annotated[ - bool | None, + bool | None | MISSING, Field(description='A flag to indicate whether the space is unfinished or not.'), - ] = None + ] = MISSING class ExteriorLightingSpace(CustomBaseModel): @@ -3915,33 +4197,38 @@ class ExteriorLightingSpace(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING description: Annotated[ - str | None, Field(description='Description of the space') - ] = None + str | None | MISSING, Field(description='Description of the space') + ] = MISSING preAltNumberFixtures: Annotated[ - int | None, Field(description='Number of fixtures to be altered', ge=0) - ] = None + int | None | MISSING, + Field(description='Number of fixtures to be altered', ge=0), + ] = MISSING numFixturesAlteredOrAdded: Annotated[ - int | None, Field(description='Number of fixtures added or altered', ge=0) - ] = None + int | None | MISSING, + Field(description='Number of fixtures added or altered', ge=0), + ] = MISSING preAltTotalWattage: Annotated[ - float | None, Field(description='Total wattage before the alteration', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Total wattage before the alteration', ge=0.0), + ] = MISSING postAltTotalWattage: Annotated[ - float | None, Field(description='Total wattage after the alteration', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Total wattage after the alteration', ge=0.0), + ] = MISSING altExemptType: Annotated[ - AltExemptTypeOptions | None, Field(description='alteration exemption type') - ] = None + AltExemptTypeOptions | None | MISSING, + Field(description='alteration exemption type'), + ] = MISSING fixture: Annotated[ - list[Fixture] | None, + list[Fixture] | MISSING, Field(description='List of fixtures in the lighting space'), - ] = None + ] = MISSING class WholeBldgUse(CustomBaseModel): @@ -3949,38 +4236,41 @@ class WholeBldgUse(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING areaDescription: Annotated[ - str | None, Field(description='Text description of the area') - ] = None + str | MISSING, Field(description='Text description of the area') + ] = MISSING floorArea: Annotated[ - float | None, Field(description='Whole building use floor area', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Whole building use floor area', ge=0.0), + ] = MISSING ceilingHeight: Annotated[ - float | None, Field(description='Average ceiling height', ge=0.0) - ] = None + float | None | MISSING, Field(description='Average ceiling height', ge=0.0) + ] = MISSING powerDensity: Annotated[ - float | None, Field(description='Internal equipment power density', ge=0.0) - ] = None + float | None | MISSING, + Field(description='Internal equipment power density', ge=0.0), + ] = MISSING internalLoad: Annotated[ - float | None, Field(description='Internal equipment load', ge=0.0) - ] = None + float | None | MISSING, Field(description='Internal equipment load', ge=0.0) + ] = MISSING constructionType: Annotated[ - ConstructionTypeOptions | None, + ConstructionTypeOptions | None | MISSING, Field(description='Construction types - compliance code specification'), - ] = None + ] = MISSING allowedWattage: Annotated[ - float | None, Field(description='Allowed wattage', ge=0.0) - ] = None + float | None | MISSING, Field(description='Allowed wattage', ge=0.0) + ] = MISSING proposedWattage: Annotated[ - float | None, Field(description='Proposed wattage', ge=0.0) - ] = None + float | None | MISSING, Field(description='Proposed wattage', ge=0.0) + ] = MISSING interiorLightingSpace: Annotated[ - InteriorLightingSpace, Field(description='Interior lighting space definition') + InteriorLightingSpace | None, + Field(description='Interior lighting space definition'), ] key: Annotated[ str | float | None, @@ -3994,11 +4284,11 @@ class WholeBldgUse(CustomBaseModel): Field(description='Interior lighting use based on space activity type'), ] isTenantSpace: Annotated[ - bool | None, + bool | None | MISSING, Field( description='A boolean to indicate whether the bldg use area is designed for tenant spaces' ), - ] = None + ] = MISSING class ExteriorUse(CustomBaseModel): @@ -4006,32 +4296,36 @@ class ExteriorUse(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING areaDescription: Annotated[ - str | None, Field(description='Text description of the area') - ] = None + str | MISSING, Field(description='Text description of the area') + ] = MISSING exteriorType: Annotated[ - ExteriorUseTypeOptions | None, Field(description='Exterior Use Type') - ] = None + ExteriorUseTypeOptions | MISSING, Field(description='Exterior Use Type') + ] = MISSING isTradable: Annotated[ - bool | None, + bool | MISSING, Field( description='Boolean flag indicate whether the exterior is tradable or not' ), - ] = None + ] = MISSING powerDensity: Annotated[ - float | None, Field(description='Internal equipment power density', ge=0.0) - ] = None - quantityUnits: Annotated[str | None, Field(description='Quantity units')] = None + float | None | MISSING, + Field(description='Internal equipment power density', ge=0.0), + ] = MISSING + quantityUnits: Annotated[ + str | None | MISSING, Field(description='Quantity units') + ] = MISSING useQuantity: Annotated[ - float | None, Field(description='The take-off quantity of the exterior use') - ] = None + float | None | MISSING, + Field(description='The take-off quantity of the exterior use'), + ] = MISSING exteriorLightingSpace: Annotated[ - ExteriorLightingSpace, Field(description='Exterior lighting space') + ExteriorLightingSpace | None, Field(description='Exterior lighting space') ] @@ -4040,11 +4334,11 @@ class Lighting(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING wholeBldgUse: Annotated[ list[WholeBldgUse], Field(description='Whole building use areas') ] @@ -4054,8 +4348,8 @@ class Lighting(CustomBaseModel): ] exteriorUse: Annotated[list[ExteriorUse], Field(description='Exterior use areas')] fixtureSchedule: Annotated[ - list[FixtureSchedule] | None, Field(description='Fixture schedules') - ] = None + list[FixtureSchedule] | MISSING, Field(description='Fixture schedules') + ] = MISSING class ComBuilding(CustomBaseModel): @@ -4063,11 +4357,11 @@ class ComBuilding(CustomBaseModel): extra='ignore', ) id: Annotated[ - str | int | None, + str | int | MISSING, Field( description='Scope-unique reference identifier for instances of this data group.' ), - ] = None + ] = MISSING control: Annotated[ Control, Field(description='project control that contains the project meta data'), @@ -4076,63 +4370,63 @@ class ComBuilding(CustomBaseModel): envelope: Annotated[Envelope, Field(description='envelope elements in the project')] location: Annotated[Location, Field(description='location of the project')] semiheated: Annotated[ - bool | None, + bool | None | MISSING, Field( description='A boolean to indicate whether the project is a semi-heated type' ), - ] = None + ] = MISSING isNonresidentialConditioning: Annotated[ bool | None, Field( description='Flag to indicate the building has conditional type of non-resindetial conditioning.' ), - ] = False + ] isResidentialConditioning: Annotated[ bool | None, Field( description='Flag to indicate the building has conditional type of resindetial conditioning.' ), - ] = False + ] isSemiheatedConditioning: Annotated[ bool | None, Field( description='Flag to indicate the building has conditional type of semiheated conditioning.' ), - ] = False + ] isHistoricBuilding: Annotated[ bool | None, Field(description='Flag to indicate if the building is historic.') ] = False performanceRating: Annotated[ - float | None, Field(description='Appendix C compliance index', ge=0.0) + float | None, Field(description='Appendix C compliance index') ] = None energyCreditPerformanceRating: Annotated[ float | None, - Field( - description='Appendix C compliance index for energy credit calculation', - ge=0.0, - ), + Field(description='Appendix C compliance index for energy credit calculation'), ] = None lighting: Annotated[Lighting, Field(description='Lighting')] - hvac: Annotated[HVAC | None, Field(description='HVAC')] = None + hvac: Annotated[HVAC | MISSING, Field(description='HVAC')] = MISSING renewable: Annotated[Renewable, Field(description='Renewable systems')] + bldgUseType: Annotated[ + BuildingUseTypeOptions | MISSING, Field(description='Building Use Type') + ] = MISSING buildingUseType: Annotated[ - BuildingUseTypeOptions | None, Field(description='Building Use Type') - ] = None + BuildingUseTypeOptions | MISSING, Field(description='Building Use Type') + ] = MISSING conditioningType: Annotated[ ConditionTypeOptions | None, Field(description='Building primary condition type'), ] = 'HEATING_AND_COOLING' swhSystem: Annotated[ - list[ServiceWaterHeatingSystem] | None, + list[ServiceWaterHeatingSystem] | MISSING, Field(description='Service water heating system', min_length=0), - ] = None + ] = MISSING efficiencyPackages: Annotated[ - list[Any] | None, + list[Any] | MISSING, Field(description='Efficiency Package or Energy Credits', min_length=0), - ] = None + ] = MISSING constructionType: Annotated[ - str | None, Field(description="Deprecated field, default to 'None'.") - ] = None + str | None | MISSING, Field(description="Deprecated field, default to 'None'.") + ] = MISSING allElectric: Annotated[ bool | int | None, Field( @@ -4163,9 +4457,7 @@ class ComBuilding(CustomBaseModel): description='Advanced reporting indicates whether the building conditioned by heat pumps' ), ] = None - projectType: Annotated[ - ProjectTypeOptions | None, Field(description='Project type') - ] = 'NEW_CONSTRUCTION' + projectType: Annotated[ProjectTypeOptions, Field(description='Project type')] projectSubType: Annotated[ ProjectSubTypeOptions | None, Field(description='Project sub-type') ] = 'CONSTRUCTION_COMPLETE' @@ -4184,8 +4476,15 @@ class ComBuilding(CustomBaseModel): FuelTypeOptions | None, Field(description='Building primary heating fuel type') ] = 'UNKNOWN_FUEL' requirements: Annotated[ - list[Requirements] | None, Field(description='Requirements', min_length=0) - ] = None + list[Requirements] | MISSING, Field(description='Requirements', min_length=0) + ] = MISSING + efficiencyPackageType: Annotated[ + EfficiencyPackageType | None, Field(description='Efficiency Package Type') + ] = None + energyCreditMultiplierException: Annotated[ + EnergyCreditMultiplierException | MISSING, + Field(description='Energy Credit Multiplier Exception'), + ] = MISSING class CheckToolJsonSchema(RootModel[ComBuilding]): diff --git a/comcheck_api/types/custom_base_model.py b/comcheck_api/types/custom_base_model.py index 8363d04..09e84da 100644 --- a/comcheck_api/types/custom_base_model.py +++ b/comcheck_api/types/custom_base_model.py @@ -1,11 +1,17 @@ import logging import re +from copy import deepcopy from typing import Any, Optional, TypeVar from pydantic.main import _model_construction -from pydantic import BaseModel +from pydantic import BaseModel, model_serializer from comcheck_api.managers.data_manager import DataManager +try: + from pydantic.experimental.missing_sentinel import MISSING as _PYDANTIC_MISSING +except ImportError: + _PYDANTIC_MISSING = None + logger = logging.getLogger(__name__) T = TypeVar("T") @@ -24,6 +30,39 @@ class CustomBaseModel(BaseModel): _identifier: str = "id" + @model_serializer(mode="plain") + def _skip_missing_fields(self): + if _PYDANTIC_MISSING is None: + return self.__dict__ + return {k: v for k, v in self.__dict__.items() if v is not _PYDANTIC_MISSING} + + def __deepcopy__(self, memo=None): + # MISSING (Sentinel) is not picklable, so copy field-by-field, passing it through as-is. + if memo is None: + memo = {} + cls = self.__class__ + new_obj = cls.__new__(cls) + memo[id(self)] = new_obj + new_dict = {} + for k, v in self.__dict__.items(): + if _PYDANTIC_MISSING is not None and v is _PYDANTIC_MISSING: + new_dict[k] = v + else: + new_dict[k] = deepcopy(v, memo) + object.__setattr__(new_obj, "__dict__", new_dict) + object.__setattr__( + new_obj, + "__pydantic_fields_set__", + deepcopy(self.__pydantic_fields_set__, memo), + ) + for attr in ("__pydantic_extra__", "__pydantic_private__"): + try: + val = object.__getattribute__(self, attr) + object.__setattr__(new_obj, attr, deepcopy(val, memo)) + except AttributeError: + pass + return new_obj + @classmethod def __pydantic_init_subclass__(cls, **kwargs): """Automatically generate `add_` methods for BaseModel-typed fields on subclasses.""" diff --git a/comcheck_api/utilities/common.py b/comcheck_api/utilities/common.py index 4e3670f..4b52675 100644 --- a/comcheck_api/utilities/common.py +++ b/comcheck_api/utilities/common.py @@ -18,7 +18,7 @@ def _json_default(obj: Any) -> Any: aliases, and nested models serialize the same way the API expects. """ if isinstance(obj, BaseModel): - return obj.model_dump(mode="json", exclude_unset=True) + return obj.model_dump(mode="json") raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable") diff --git a/comcheck_api/utilities/project_utilities.py b/comcheck_api/utilities/project_utilities.py index 7ebe4e7..67bf54e 100644 --- a/comcheck_api/utilities/project_utilities.py +++ b/comcheck_api/utilities/project_utilities.py @@ -6,6 +6,35 @@ from comcheck_api.managers.data_manager import DataManager, get_model_info +def _require_unique_area_description( + project: ComBuilding, + area_description: str, + exclude_key: str | float | None = None, +) -> None: + """Raise if area_description already exists in wholeBldgUse (case-sensitive). + + Args: + project: The project to check. + area_description: The description to validate for uniqueness. + exclude_key: Skip the WholeBldgUse item with this key (used during updates + so the item being edited doesn't conflict with itself). + + Raises: + ValueError: If another WholeBldgUse item has the same areaDescription. + """ + whole_use = project.get_by_path("lighting.wholeBldgUse") + if not isinstance(whole_use, list): + return + + for area in whole_use: + if exclude_key is not None and getattr(area, "key", None) == exclude_key: + continue + if getattr(area, "areaDescription", None) == area_description: + raise ValueError( + f"areaDescription '{area_description}' already exists in wholeBldgUse." + ) + + def _require_building_area(project: ComBuilding, building_area_key: str) -> None: """ Ensure that project.lighting.wholeBldgUse exists and contains the given key. @@ -21,6 +50,50 @@ def _require_building_area(project: ComBuilding, building_area_key: str) -> None ) +def _require_activity_use( + project: ComBuilding, building_area_key: str, area_description: str +) -> None: + """Ensure the given building area contains an activityUse with the given areaDescription.""" + whole_use = project.get_by_path("lighting.wholeBldgUse") + if not isinstance(whole_use, list): + raise ValueError("No building areas (wholeBldgUse) found in project.") + + area = next( + (area for area in whole_use if getattr(area, "key", None) == building_area_key), + None, + ) + if area is None: + raise ValueError( + f"Building area key '{building_area_key}' not found in lighting.wholeBldgUse." + ) + + activity_uses = getattr(area, "activityUse", []) or [] + if not any( + getattr(activity_use, "areaDescription", None) == area_description + for activity_use in activity_uses + ): + raise ValueError( + f"ActivityUse with areaDescription '{area_description}' " + f"not found in building area '{building_area_key}'." + ) + + +def _require_exterior_use(project: ComBuilding, area_description: str) -> None: + """Ensure project.lighting.exteriorUse contains an ExteriorUse with the given areaDescription.""" + exterior_uses = project.get_by_path("lighting.exteriorUse") + if not isinstance(exterior_uses, list): + raise ValueError("No exterior uses (lighting.exteriorUse) found in project.") + + if not any( + getattr(exterior_use, "areaDescription", None) == area_description + for exterior_use in exterior_uses + ): + raise ValueError( + f"ExteriorUse with areaDescription '{area_description}' " + f"not found in lighting.exteriorUse." + ) + + def find_component_in_component_list( components: List[CustomBaseModel], component_id: str ): diff --git a/docs/schema.md b/docs/schema.md new file mode 100644 index 0000000..a934724 --- /dev/null +++ b/docs/schema.md @@ -0,0 +1,944 @@ +# Schema Reference + +This document covers three related topics: + +1. **Runtime compatibility** — known gaps between the server's actual behavior and the generated models, and how they are handled automatically. +2. **Authoring guidelines** — lessons learned when modifying `comCheck.schema.json` or re-running Pydantic model generation. +3. **Changelog** — every meaningful change introduced in the schema update merged via PR #25. + +--- + +## Background: `MISSING` Sentinel + +Many fields in the generated models use `MISSING` (from `pydantic.experimental.missing_sentinel`) as a default instead of `None`. A field with `= MISSING` means: + +- **On parse:** the server did not include this field — the model holds `MISSING` rather than failing validation. +- **On serialize:** `model_dump(mode='json')` omits the key entirely — the server does not receive it. + +This is the intentional "sparse update" pattern: only fields the server actually sent are round-tripped back. Issues arise when the server *requires* a field on write but omits it on read, or when `MISSING` leaks into the JSON payload. + +--- + +## Part 1: Runtime Compatibility + +The `comcheck_api` library bridges between the Python Pydantic models in `core_types.py` (generated from `comCheck.schema.json`) and the live COMcheck backend API. Because the server may return data that predates or diverges from the current schema, `CustomBaseModel` contains several sanitization layers that run automatically on every parse and serialize cycle. + +### Issue 1 — `deepcopy` fails on models with `MISSING` fields + +**Symptom:** `TypeError: Cannot pickle 'Sentinel' object` when calling `copy.deepcopy()` or `model.model_copy(deep=True)`. + +**Root cause:** `MISSING` is a `typing_extensions.Sentinel` that is not picklable. Pydantic's `__deepcopy__` internally uses pickle for nested objects. + +**Fix:** `CustomBaseModel.__deepcopy__` copies fields one-by-one, passing `MISSING` through unchanged rather than attempting to deep-copy it. + +--- + +### Issue 2 — Boolean flags serialized as integers + +**Symptom:** `HTTP 400: 'instance.isHistoricBuilding' is not of a type(s) boolean` + +**Root cause:** The generated schema models boolean flags as `IntEnum` with values `{0, 1}` (e.g. `IsHistoricBuilding`, `CirculationPump`, `HeatTraceTapeInstalled`, `CombinedSystem`, `PoolSystem`). `model_dump(mode='json')` serializes them as integers; the server's JSON Schema validator requires `true`/`false`. + +**Affected fields:** `isHistoricBuilding`, `allElectric`, `isRenewable`, `hasBattery`, `hasCharger`, `hasHeatPump` + +**Fix:** `CustomBaseModel.model_dump` runs both a `mode='python'` and `mode='json'` dump, then walks them in parallel. Wherever the Python value is an `IntEnum` instance, the corresponding JSON integer is replaced with `bool(value)`. + +--- + +### Issue 3 — Unknown enum values crash on parse + +**Symptom:** `ValidationError: Input should be 'ACTIVITY_INVALID_USE', 'ACTIVITY_AUTO_REPAIR', ... [type=enum]` for a value like `'ACTIVITY_COMMON_OFFICE'`. + +**Root cause:** Projects created under older schema versions may reference enum values that have since been renamed or removed (e.g. `ACTIVITY_COMMON_OFFICE` → `ACTIVITY_COMMON_OFFICE_OPEN`). The server stores these values verbatim and returns them unchanged. + +**Fix — two-validator approach:** + +Pydantic runs validators in order: `wrap` → `before` → field validation → `after`. This ordering is used to preserve the original value while still satisfying Pydantic's type checker: + +1. **`_preserve_invalid_enum_strings` (`mode='wrap'`)** runs first. It inspects the raw input dict and stashes any string values that are not valid members of their declared `StrEnum` type. +2. **`_sanitize_server_data` (`mode='before'`)** runs next (inside the `handler` call). It replaces the unknown string with the first valid enum member so Pydantic can construct the model without raising a `ValidationError`. +3. After `handler()` returns the constructed model object, `_preserve_invalid_enum_strings` writes the original (unknown) string back onto the field using `object.__setattr__`, bypassing field validation entirely. + +The result: the model field holds exactly what the server sent. `model_dump(mode='json')` serializes it as-is, so the value round-trips back to the server unchanged. A `WARNING` is still logged so the drift is visible. + +```python +# Server returns an old enum value +project = client.get_project("project-id") +area = project.lighting.wholeBldgUse[0].activityUse[0] + +print(area.activityType) # 'ACTIVITY_COMMON_OFFICE' ← original value preserved +print(type(area.activityType)) # ← not an enum member +``` + +> **Schema action needed:** When the backend migrates old records to use current enum values, this fallback will no longer be triggered. + +--- + +### Issue 4 — Pydantic serializer `UserWarning` spam + +**Symptom:** Dozens of `PydanticSerializationUnexpectedValue: Expected 'MISSING' sentinel` warnings on every `model_dump` call. + +**Root cause:** Pydantic's Rust serializer is compiled with the full field schema. When a model instance has `MISSING`-valued fields in `__dict__`, it sees a field count mismatch and warns. + +**Fix:** `CustomBaseModel` defines a `model_serializer(mode='plain')` that filters out any `MISSING`-valued entries before the Rust serializer sees them. The serialization output is identical — MISSING fields were already excluded — but the warning is eliminated. + +--- + +### Issue 5 — Server sends negative sentinels for unset numeric fields + +**Symptom:** `HTTP 400: 'instance.envelope.roof[1].continuousRValue' must be greater than or equal to 0` + +**Root cause:** The server uses `-1` (and similar negative values like `-4.545`) as a "not set" sentinel on fields like `continuousRValue`, `propUValue`, `cavityRValue`. The server enforces `>= 0` on write but does not enforce it on read. + +**Fix:** `CustomBaseModel._sanitize_server_data` replaces any incoming negative number with the field's declared `default` value, when that default is `>= 0`. A warning is logged. + +**Schema fix applied:** Added `"minimum": 0.0` to the following fields in `comCheck.schema.json` where it was missing: + +| Definition | Fields updated | +|---|---| +| `AboveGradeWall` (all 3 definitions) | `cavityRValue`, `continuousRValue`, `propUValue`, `grossArea` | +| `BelowGradeWall` | `cavityRValue`, `continuousRValue` | +| `Window` | `cavityRValue`, `continuousRValue` | +| `Door` | `cavityRValue`, `continuousRValue` | +| `Skylight` | `cavityRValue`, `continuousRValue` | +| `Roof` (second definition) | `cavityRValue`, `continuousRValue`, `propUValue` | + +--- + +### Issue 6 — `MISSING` fields included in outbound JSON payload + +**Symptom:** `HTTP 400: 'instance.lighting.wholeBldgUse[0].allowedWattage' is not of a type(s) number` + +**Root cause:** `MISSING` fields — those the server never sent — were being included in `model_dump(mode='json')` output as unexpected non-typed values (not `null`, not a number). The server's JSON Schema validator rejected them. + +**Fix:** The `model_dump` post-processing step (which already diffs Python vs JSON output for Issue 2) now also drops any key whose Python-side value is `MISSING`. The field is simply omitted from the outbound payload. + +--- + +### Issue 7 — `allowanceType` absent on GET, required on PUT + +**Symptom:** `ValidationError: Field required [type=missing]` on parse, then `HTTP 400: requires property "allowanceType"` on PUT. + +**Root cause:** The server omits `allowanceType` for older envelope records (sends `null` or omits the key entirely), but its write validator requires the field to be present with a valid string value. The field type is `EnvelopeAssemblyAllowanceTypeOptions | MISSING`. + +**Affected components:** `AgWall`, `BgWall`, `Window`, `Door`, `Skylight`, `Roof` + +**Fix — inbound (parse):** `_sanitize_server_data` maps an incoming `null` for a non-nullable enum field to the first non-null enum member. For `EnvelopeAssemblyAllowanceTypeOptions`, this is `ENV_ALLOWANCE_NONE`. + +**Fix — schema:** Removed `allowanceType` from the `required` arrays of all six component definitions in `comCheck.schema.json`. The field remains defined in `properties` — it is optional on read, required on write (enforced by the server). + +> **Note:** This is a server-side inconsistency. The long-term fix is for the server to always populate `allowanceType` when returning records, and for the schema to reflect that it is always present. Once the server is updated, the `required` entries can be restored. + +--- + +### Issue 8 — `null` values dropped for non-optional enum fields + +**Symptom:** Fields like `adjacentSpaceType`, `exemptionType` (typed as `SomeEnum`, not `SomeEnum | None`) arrive as `null` from the server, causing the Pydantic model to lose them. + +**Root cause:** The server sends `null` for fields it considers "not set" even when the generated schema does not allow `null`. The `_sanitize_server_data` validator was dropping these fields (reverting to `MISSING`), which then caused serialization failures or missing required fields on write. + +**Fix:** When the field has an enum with a `None`-valued member (`NoneType_None = None`), the incoming `null` is mapped to that enum member rather than dropped. When no such member exists, the field is dropped and a warning is logged. + +--- + +### Logging + +All sanitization actions emit `WARNING`-level log messages via `comcheck_api.types.custom_base_model`. To see them: + +```python +import logging +logging.basicConfig(level=logging.WARNING) +``` + +Example output: +``` +WARNING comcheck_api.types.custom_base_model:custom_base_model.py:142 + Replacing unknown enum value 'ACTIVITY_COMMON_OFFICE' with fallback 'ACTIVITY_INVALID_USE' + for field ActivityUse.activityType + +WARNING comcheck_api.types.custom_base_model:custom_base_model.py:160 + Replacing server sentinel -1 with default 0.0 for field Roof.continuousRValue +``` + +--- + +## Part 2: Schema Authoring Guidelines + +Takeaways from the most recent round of changes to `comcheck_api/schemas/comCheck.schema.json` and the Pydantic model generation. + +### 1. Use `--use-missing-sentinel` for optional fields + +Passing `--use-missing-sentinel` to the generator lets fields be marked with a `MISSING` identifier instead of defaulting to a JSON value. When the model is exported back to JSON, `MISSING` fields are omitted entirely. + +**Why it matters:** we were hitting problems where fields that weren't present in the original export would default to values we didn't want. The sentinel avoids inventing data — absent stays absent on round-trip. + +### 2. Avoid adding redundant `NONE` to enumerations + +Adding `NONE` to enums caused a lot of churn, and in many cases an option that already means "none" existed. Example: `SlabInsulationPositionOptions` already has `NO_INSULATION` **and** `NONE`. + +**Action:** before adding `NONE`, check whether the enum already has an equivalent member and reuse it rather than introducing a duplicate. + +### 3. Allow `null` as a valid type for many fields + +A lot of values arrive as `null` and should *not* be coerced to a default. To handle this correctly we had to explicitly allow `null` as a type for many fields in the JSON schema. + +### 4. Exemption types and activity types are likely incomplete + +A number of exemption types and activity types were added in PR #25, but coverage may not be complete. + +**Action:** cross-reference the enumeration directly in the backend code to confirm all valid exemption/activity types are represented. + +### 5. Put `null` inside the enumeration instead of `anyOf: [enum, null]` + +Rather than repeating `anyOf: [enumeration, null]` across many fields, add `null` directly to the enumeration itself. This avoids repetition and is simpler to write. + +**Note:** when the enum carries the type, you can also drop the `type` keyword on the field — the type is inferred from the enumeration. + +### 6. Reconsider `minimum` constraints + +`minimum` flags caused failures — e.g. a building with `preAltPropUval` less than 0 failed schema validation and couldn't be loaded into the `ComBuilding` object. + +**Open question:** do we actually need `minimum` in the schema? It isn't necessarily enforced by the backend, and its main effect right now is blocking otherwise-valid projects from loading. Consider removing these unless the constraint is genuinely required. + +### 7. Drop non-informative descriptions; prefer `title` + +Many `description` fields just restate the field name and add nothing. + +**Action:** remove descriptions that don't add information. If the text is just a formatted version of the field name, use the `title` keyword instead of `description`. + +--- + +## Part 3: Changelog — PR #25 + +Every meaningful change introduced in the schema update merged via PR #25. Changes are grouped by category. + +--- + +### 1. Field Additions + +#### `ComBuilding` + +| Field | Type | Notes | +|---|---|---| +| `bldgUseType` | `$ref BuildingUseTypeOptions` | Legacy alias for `buildingUseType`. Comment: "Legacy enum, only ACTIVITY is valid in the new ComCheck Web." | +| `efficiencyPackageType` | `enum` (string or `null`) | New field. Values: `EFF_PACKAGE_UNKNOWN`, `EFF_PACKAGE_HVAC_PERFORMANCE`, `EFF_PACKAGE_LIGHTING_REDUCED_LPD`, `EFF_PACKAGE_REDUCED_AIR_INFILTRATION`, `EFF_PACKAGE_ENHANCED_ENVELOPE_PERFORMANCE`, `EFF_PACKAGE_ENHANCED_LIGHTING_CONTROLS`, `EFF_PACKAGE_ONSITE_RENEWABLES`, `null`. Default: `null`. | +| `energyCreditMultiplierException` | `enum` (or `null`) | New field. Values: `NO_ENERGY_CREDIT_MULTIPLIER_EXCEPTION`, `ENERGY_CREDIT_MULTIPLIER_EXCEPTION_LOW_ENERGY_BUILDINGS`, `ENERGY_CREDIT_MULTIPLIER_EXCEPTION_PRIMARY_HEAT_PUMP`, `null`. No default declared. | + +#### `Door` (fenestration) + +| Field | Type | Notes | +|---|---|---| +| `feetAg` | `["number", "null"]` | New field: feet above grade. `minimum: 0.0`, `default: null`. | + +#### `Skylight` + +| Field | Type | Notes | +|---|---|---| +| `cavityRValue` | `["number", "null"]` | New field: average insulation R-value in cavity. Unit: `h-ft2-F/Btu`, `default: 0.0`. | +| `continuousRValue` | `["number", "null"]` | New field: continuous insulation on the skylight. Unit: `h-ft2-F/Btu`, `default: 0.0`. | + +#### `HVACSystem` + +| Field | Type | Notes | +|---|---|---| +| `requirementAnswer` | `array` of `$ref Requirements` | New field. `default: []`. | + +#### `HVACPlant` + +| Field | Type | Notes | +|---|---|---| +| `requirementAnswer` | `array` of `$ref Requirements` | New field. `default: []`. | + +#### `InteriorLightingFixture` + +| Field | Type | Notes | +|---|---|---| +| `scheduleFixtureKey` | `["string", "null"]` | New field: UUID to identify this fixture schedule. | +| `typeOfFixture` | `["string", "null"]` | New field: type of the fixture. | + +--- + +### 2. Removed Fields / Enum Values Removed + +#### `ActivityTypeOptions` + +| Removed value | Notes | +|---|---| +| `ACTIVITY_COMMON_OFFICE` | Removed from enum. `ACTIVITY_COMMON_OFFICE_ENCLOSED` and `ACTIVITY_COMMON_OFFICE_OPEN` remain. | + +--- + +### 3. Type Changes + +#### `ComBuilding` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `isHistoricBuilding` | `"boolean"` | `"integer"`, `enum: [0, 1]` | Changed from boolean to integer flag. | +| `isNonresidentialConditioning` | `"boolean"` | `["boolean", "null"]` | Made nullable. | +| `isResidentialConditioning` | `"boolean"` | `["boolean", "null"]` | Made nullable. | +| `isSemiheatedConditioning` | `"boolean"` | `["boolean", "null"]` | Made nullable. | +| `constructionType` | `"string"` | `["string", "null"]` | Made nullable. | + +#### `CodeData` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `version` | `"string"` | `["string", "null"]` | Made nullable. | + +#### `AgWall` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `heatCapacity` | `"number"` | `["number", "null"]` | Made nullable. | +| `cavityRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `continuousRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `propUValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `grossArea` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `BgWall` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `heatCapacity` | `"number"` | `["number", "null"]` | Made nullable. | +| `cavityRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `continuousRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `propUValue` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `Window` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `propUValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `grossArea` | `"number"` | `["number", "null"]` | Made nullable. | +| `propShgc` | `"number"` | `["number", "null"]` | Made nullable. | +| `preAltPropShgc` | `"number"` | `["number", "null"]` | Made nullable. | +| `cavityRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `continuousRValue` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `Door` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `propUValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `propShgc` | `"number"` | `["number", "null"]` | Made nullable. | +| `preAltPropShgc` | `"number"` | `["number", "null"]` | Made nullable. | +| `cavityRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `continuousRValue` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `Skylight` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `propUValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `grossArea` | `"number"` | `["number", "null"]` | Made nullable. | +| `propShgc` | `"number"` | `["number", "null"]` | Made nullable. | +| `preAltPropShgc` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `Roof` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `cavityRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `continuousRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `propUValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `grossArea` | `"number"` | `["number", "null"]` | Made nullable. | +| `purlinSpacing` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `Floor` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `cavityRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `continuousRValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `propUValue` | `"number"` | `["number", "null"]` | Made nullable. | +| `grossArea` | `"number"` | `["number", "null"]` | Made nullable. | +| `slabFullInsulBelowMinRValue` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `WholeBldgUse` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `floorArea` | `"number"` | `["number", "null"]` | Made nullable. | +| `powerDensity` | `"number"` | `["number", "null"]` | Made nullable. | +| `internalLoad` | `"number"` | `["number", "null"]` | Made nullable. | +| `allowedWattage` | `"number"` | `["number", "null"]` | Made nullable. | +| `proposedWattage` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `ActivityUse` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `floorArea` | `"number"` | `["number", "null"]` | Made nullable. | +| `ceilingHeight` | `"number"` | `["number", "null"]` | Made nullable. | +| `powerDensity` | `"number"` | `["number", "null"]` | Made nullable. | +| `internalLoad` | `"number"` | `["number", "null"]` | Made nullable. | +| `allowedWattage` | `"number"` | `["number", "null"]` | Made nullable. | +| `proposedWattage` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `ExteriorUse` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `powerDensity` | `"number"` | `["number", "null"]` | Made nullable. | +| `useQuantity` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `InteriorLightingSpace` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `numFixturesAlteredOrAdded` | `["integer", "null"]` with `minimum: 0` | `["integer", "null"]` | `minimum` constraint removed. | +| `primaryDaylight` | `"number"` | `["number", "null"]` | Made nullable. | +| `secondaryDaylight` | `"number"` | `["number", "null"]` | Made nullable. | +| `skylightToplight` | `"number"` | `["number", "null"]` | Made nullable. | +| `roofMonitorToplight` | `"number"` | `["number", "null"]` | Made nullable. | +| `decorativeArea` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `InteriorLightingFixture` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `fixtureType` | `"string"` | `["string", "null"]` | Made nullable. | +| `fixtureWattage` | `"number"` | `["number", "null"]` | Made nullable. | +| `quantity` | `"integer"` | `["integer", "null"]` | Made nullable. | +| `quantityWithAdvControls` | `"integer"` | `["integer", "null"]` | Made nullable. | + +#### `FixtureSchedule` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `id` | `"integer"` | `["string", "integer"]` | Now also accepts string. | +| `lightingId` | `"integer"` | `["string", "integer"]` | Now also accepts string. | + +#### `HVAC` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `fanSystem` | `"array"` | `["array", "null"]` | Made nullable. Added `default: null`. Description capitalised from "fan system" to "Fan system". | + +#### `HVACSystem` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `quantity` minimum | `1` | `0` | Minimum quantity lowered from 1 to 0. | + +#### `HVACPlant` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `condenserFlowRate` | `"number"` | `["number", "null"]` | Made nullable. | +| `condenserLeavingTemperature` | `"number"` | `["number", "null"]` | Made nullable. | +| `coolingPlantCapacity` | `"number"` | `["number", "null"]` | Made nullable. | +| `enteringCondenserWaterTemperature` | `"number"` | `["number", "null"]` | Made nullable. | +| `evaporatorLeavingTemperature` | `"number"` | `["number", "null"]` | Made nullable. | +| `heatingPlantCapacity` | `"number"` | `["number", "null"]` | Made nullable. | +| `heatRecovery` | `"boolean"` | `["integer", "null"]`, `enum: [0, 1, null]` | Changed from boolean to integer flag with null support. | +| `heatPumpSimultaneousCoolingAndHeating` | `"boolean"` | `["integer", "null"]`, `enum: [0, 1, null]` | Changed from boolean to integer flag with null support. | +| `leavingChilledWaterTemperature` | `"number"` | `["number", "null"]` | Made nullable. | +| `propCoolingPlantEfficiencyPartial` | `"number"` | `["number", "null"]` | Made nullable. | +| `propCoolingPlantEfficiency` | `"number"` | `["number", "null"]` | Made nullable. | +| `propHeatingPlantEfficiency` | `"number"` | `["number", "null"]` | Made nullable. | +| `quantity` minimum | `1.0` | `0` | Minimum quantity lowered from 1 to 0. | +| `systemType` | `"string"` | `["string", "null"]` | Made nullable. | +| `twoPipeSystem` | `"boolean"` | `["integer", "null"]`, `enum: [0, 1, null]` | Changed from boolean to integer flag with null support. | +| `waterloopHeatPump` | `"boolean"` | `["integer", "null"]`, `enum: [0, 1, null]` | Changed from boolean to integer flag with null support. | + +#### `FanSystem` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `description2` | `"string"` | `["string", "null"]` | Made nullable. | +| `fanSystemKey` | `"string"` | `["string", "null"]` | Made nullable. | +| `hasPressureDropCredits` | `["boolean", "integer"]` | `enum: [0, 1, null]` | Changed to nullable enum. | + +#### `Fan` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `fanDesignEfficiency` | `"number"` | `["number", "null"]` | Made nullable. | +| `maxNameplateHp` | `"number"` with `minimum: 0.0` | `["number", "null"]` | Made nullable; `minimum` constraint removed. | +| `nameplateHp` | `"number"` with `minimum: 0.0` | `"number"` | `minimum` constraint removed (type unchanged). | +| `totalFanEfficiency` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `PressureDrop` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `recoveryEffectiveness` | `"number"`, `minimum: 0.0`, `maximum: 1.0` | `["number", "null"]`, `minimum: 0.0` | Made nullable; `maximum: 1.0` constraint removed. | +| `verticalDuctLength` | `"number"` | `["number", "null"]` | Made nullable. | + +#### `ServiceWaterHeatingSystem` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `circulationPump` | `"boolean"` | `"integer"`, `enum: [0, 1]`, `default: 0` | Changed from boolean to integer flag. | +| `heatTraceTapeInstalled` | `"boolean"` | `"integer"`, `enum: [0, 1]`, `default: 0` | Changed from boolean to integer flag. | +| `combinedSystem` | `"boolean"` | `"integer"`, `enum: [0, 1]`, `default: 0` | Changed from boolean to integer flag. | +| `poolSystem` | `"boolean"` | `"integer"`, `enum: [0, 1]`, `default: 0` | Changed from boolean to integer flag. | +| `heatPumpPoolHeater` (renamed from `heatpumpPoolHeater`) | `"boolean"` | `["boolean", "null"]`, `enum: [0, 1, null]`, `default: null` | Renamed (camelCase fix) and made nullable. | +| `quantity` minimum | `1` | `0` | Minimum quantity lowered from 1 to 0. | +| `requirementAnswer` | `"array"` (no items defined) | `"array"` with `items: $ref Requirements`, `default: []` | Items type now specified. | + +#### `EnergyCreditPackage` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `bldgUseKey` | `"string"` | `["string", "null"]` | Made nullable. | + +#### `Renewable` + +| Field | Old type | New type | Notes | +|---|---|---|---| +| `numberOfFloors` minimum | `1` | `0` | Minimum floors lowered from 1 to 0. | +| `largestThreeFloorArea` | `"number"` | `["number", "null"]` | Made nullable. | +| `requiredCapacity` | `"number"` | `["number", "null"]` | Made nullable. | +| `proposedCapacity` | `"number"` | `["number", "null"]` | Made nullable. | +| `roofAreaForRenewable` | `"number"` | `["number", "null"]` | Made nullable. | + +--- + +### 4. Constraint Changes + +#### `ComBuilding` + +| Field | Change | +|---|---| +| `performanceRating` | `minimum: 0.0` removed. | +| `energyCreditPerformanceRating` | `minimum: 0.0` removed. | + +#### `AgWall` + +| Field | Change | +|---|---| +| `cavityRValue` | `minimum: 0.0` removed. | +| `continuousRValue` | `minimum: 0.0` removed. | +| `continuousDeratedRValue` | `default: 0.0` removed (now has no default). | +| `propUValue` | `minimum: 0.0` removed. | +| `grossArea` | `minimum: 0.0` removed. | + +#### `BgWall` + +| Field | Change | +|---|---| +| `cavityRValue` | `minimum: 0.0` removed. | +| `continuousRValue` | `minimum: 0.0` removed. | +| `propUValue` | `minimum: 0.0` removed. | + +#### `Window` + +| Field | Change | +|---|---| +| `propUValue` | `minimum: 0.0` removed. | +| `propShgc` | No constraint change (minimum still 0.0). | +| `preAltPropUval` | `default: 0.0` removed. | +| `cavityRValue` | `minimum: 0.0` removed. | +| `continuousRValue` | `minimum: 0.0` removed. | + +#### `Door` + +| Field | Change | +|---|---| +| `propUValue` | `minimum: 0.0` removed. | +| `preAltPropUval` | `default: 0.0` removed. | +| `cavityRValue` | `minimum: 0.0` removed. | +| `continuousRValue` | `minimum: 0.0` removed. | + +#### `Skylight` + +| Field | Change | +|---|---| +| `propUValue` | `minimum: 0.0` removed. | +| `preAltPropUval` | `default: 0.0` removed. | + +#### `Roof` + +| Field | Change | +|---|---| +| `highAlbedoRoofReqType` | Typo `"defualt"` corrected to `"default"`. | +| `cavityRValue` | `minimum: 0.0` removed. | +| `continuousRValue` | `minimum: 0.0` removed. | +| `propUValue` | `minimum: 0.0` removed. | + +#### `Floor` + +| Field | Change | +|---|---| +| `cavityRValue` | Type changed to nullable. | +| `propUValue` | `minimum: 0.0` removed. | + +#### `InteriorLightingSpace` + +| Field | Change | +|---|---| +| `numFixturesAlteredOrAdded` | `minimum: 0` removed. | + +#### `HVACSystem` + +| Field | Change | +|---|---| +| `quantity` | `minimum` changed from `1` to `0`. | + +#### `HVACPlant` + +| Field | Change | +|---|---| +| `quantity` | `minimum` changed from `1.0` to `0`. | +| `efficiencyRequirementException` | `default: "EFF_EXCEPTION_UNSPECIFIED"` removed. | + +#### `ServiceWaterHeatingSystem` + +| Field | Change | +|---|---| +| `quantity` | `minimum` changed from `1` to `0`. | +| `swhSystemSubType` | `default: "UNKNOWN_SWH_SYSTEM_SUB_TYPE"` removed. | +| `efficiencyRequirementException` | `default: "EFF_EXCEPTION_UNSPECIFIED"` removed. | + +#### `Envelope` (`useOrientationDetails`) + +| Field | Change | +|---|---| +| `useOrientationDetails` | `default: true` replaced with `const: true`. This field must now always equal `true`. | + +--- + +### 5. Structural / `$ref` Changes + +#### `anyOf` → direct `$ref` (null support moved into enum) + +Several fields that previously used `anyOf: [{type: null}, {$ref: ...}]` have been changed to a bare `$ref`. Nullability is now provided by the enum definition itself (which has had `null` added as an enum value). + +| Definition | Field | +|---|---| +| `AgWall` | `adjacentSpaceBuildingType` | +| `AgWall` | `allowanceType` | +| `BgWall` | `adjacentSpaceBuildingType` | +| `BgWall` | `allowanceType` | +| `Roof` | `adjacentSpaceBuildingType` | +| `Roof` | `allowanceType` | +| `Window` | `adjacentSpaceBuildingType` | +| `Window` | `allowanceType` | +| `Window` | `frameType` | +| `Door` | `adjacentSpaceBuildingType` | +| `Door` | `allowanceType` | +| `Door` | `frameType` | +| `Skylight` | `adjacentSpaceBuildingType` | +| `Skylight` | `allowanceType` | +| `Skylight` | `frameType` | +| `Floor` | `allowanceType` | +| `InteriorLightingFixture` | `allowanceType` | +| `InteriorLightingFixture` | `ballast` | +| `InteriorLightingFixture` | `trackLightingWattageBasisType` | +| `FixtureSchedule` | `trackLightingWattageBasisType` | + +#### `AgWall.otherWallType` + +Changed from bare `$ref AgWallOtherTypeOptions` to `anyOf: [{type: null}, {$ref: ...}]`. Null is now explicitly permitted. + +#### `WholeBldgUse.interiorLightingSpace` + +Changed from bare `$ref InteriorLightingSpace` to `anyOf: [{$ref: ...}, {type: null}]`. Null is now explicitly permitted. + +#### `ActivityUse.interiorLightingSpace` + +Changed from bare `$ref InteriorLightingSpace` to `anyOf: [{$ref: ...}, {type: null}]`. Null is now explicitly permitted. + +#### `ExteriorUse.exteriorLightingSpace` + +Changed from bare `$ref ExteriorLightingSpace` to `anyOf: [{$ref: ...}, {type: null}]`. Null is now explicitly permitted. + +#### `InteriorLightingFixture.advControlAllowanceType` → renamed to `advControlsAllowanceType` + +Field renamed from `advControlAllowanceType` to `advControlsAllowanceType`. Also changed from bare `$ref` with no default to `$ref` with `default: null`. + +--- + +### 6. `required` Array Changes + +#### `InteriorLightingFixture` + +| Change | Notes | +|---|---| +| `lightingType` removed from required | `lightingType` is no longer required. | +| `fixtureType` added to required | `fixtureType` is now required (replacing `lightingType`). | + +#### `FixtureSchedule` + +| Change | Notes | +|---|---| +| `lightingType` removed from required | `lightingType` is no longer required. | + +*All other `required` array changes in the diff are purely formatting (inline → multi-line) with no semantic difference.* + +--- + +### 7. New Enum Values + +#### `EnergyCodeOptions` + +Added: `CEZ_IECC2009`, `CEZ_IECC2012`, `CEZ_IECC2024_APPXCF`, `CEZ_90_1_2007`, `CEZ_90_1_2010`, `NONE` + +#### `StateRegionEnergyCodeOptions` + +Added: `CEZ_NYS2024_IECC2024`, `CEZ_NYS2025_9012022`, `CEZ_NYC2025_IECC2024`, `CEZ_NYC2025_9012022`, `CEZ_VT2024_IECC2021`, `CEZ_LA2021_IECC2021`, `NONE` + +#### `ProjectTypeOptions` + +Added: `NONE`, `null`. Also dropped the explicit `"type": "string"` constraint. + +#### `AirBarrierComplianceTypeOptions` + +Added: `AIR_BARRIER_OPTION_CONTINUITY_PLAN` + +#### `AgWallTypeOptions` + +Added: `OTHER_BG_WALL`, `OTHER_FRAME`, `null`. `METAL_BLDG_AG_WALL` description changed from "Metal Building Wall" to "Metal Building Wall Without Thermal Break". + +#### `BgWallTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `RoofTypeOptions` + +Added: `METAL_ROOF_W_THERMAL_BREAK` + +#### `HighAlbedoRoofReqTypeOptions` + +Added: `HA_ROOF_REQ_SOLAR_REFLECTANCE` + +#### `FloorTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `SlabInsulationPositionOptions` + +Added: `NONE` as an additional alias. + +#### `AgWallConstructionDetailsTypeOptions` + +Added: `AG_WALL_CONSTRUCTION_DETAILS_UNKNOWN`, `AG_WALL_CONSTRUCTION_DETAILS_HORIZONTAL_Z_GIRTS`, `AG_WALL_CONSTRUCTION_DETAILS_VERTICAL_Z_GIRTS`, `AG_WALL_CONSTRUCTION_DETAILS_Z_GIRTS_THERMAL_BROKEN` + +#### `EnvelopeAssemblyAllowanceTypeOptions` + +Added: `NONE`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `CMUTypeOptions` + +Added: `NONE`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `ConcreteDensityOptions` + +Added: `85`, `135`, `null`. Dropped explicit `"type": "integer"` constraint. + +#### `ConcreteThicknessOptions` + +Added: `3`, `4`, `5`, `7`, `9`, `11`, `null`. Dropped explicit `"type": "integer"` constraint. + +#### `EnvelopeAssemblyExemptionOptions` + +Added: `NONE` + +#### `FurringTypeOptions` + +Added: `NONE` at the beginning of the enum. + +#### `OrientationOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `AltExemptTypeOptions` + +Added: `EXEMPT_HISTORIC_CHARACTERISTIC`, `EXEMPT_LIGHTING_SPACE_REPLACEMENT_LT_20_PCT_LOAD` + +#### `FenestrationFrameTypeOptions` + +Added: `NON_METAL`, `NONE`, `METAL_FRAME_24_AG_WALL`, `GLASS_DOOR`, `METAL_THERMAL_BREAK`, `OTHER_DOOR`, `INSUL_METAL_DOOR`, `NO_INSUL_SINGLE_METAL_DOOR`, `WOOD_FRAME_16_AG_WALL`, `ALL_WOOD_JOIST_TRUSS_FLOOR`, `METAL_FRAME_16_AG_WALL`, `WOOD_DOOR`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `GlazingTypeOptions` + +Added: `OTHER_GLAZING`, `NONE` + +#### `SolarTypeOptions` + +Added: `NONE` + +#### `PerfDataTypeOptions` + +Added: `NONE`, `PERF_TYPE_UNSPECIFIED` + +#### `GlazingMaterialTypeOptions` + +Added: `NONE` + +#### `DoorTypeOptions` + +Added: `METAL_W_THERMAL_BREAK` + +#### `LightingAllowanceTypeOptions` + +Added: `ALLOWANCE_ADVANCED_CONTROLS`, `ALLOWANCE_DECORATIVE_APPEARANCE_LOBBIES`, `ALLOWANCE_DECORATIVE_APPEARANCE_OTHER`, `ALLOWANCE_ELECTRICAL_MECHANICAL`, `ALLOWANCE_VIDEO_CONFERENCE`, `NONE`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `LightingExemptionTypeOptions` + +Added: `EXEMPTION_APPROVED_SAFETY`, `EXEMPTION_DWELL_UNIT_CONTROLLED`, `EXEMPTION_EMERGENCY_AUTOOFF`, `EXEMPTION_HIGHLIGHT_HAZARDS`, `EXEMPTION_INDUSTRIAL_PRODUCTION`, `EXEMPTION_MANUFACTURER_AS_PART_OF_EQUIP`, `EXEMPTION_POOLS_WATER`, `EXEMPTION_REQUIRED_EGRESS`, `EXEMPTION_TEMP_LIGHTING`, `EXEMPTION_THEME_PARK_ELEMENTS`, `EXEMPTION_HIGHLIGHT_MONUMENT`, `EXEMPTION_TRANSPORTATION_MARKER`, `EXEMPTION_TRANSPORTATION_SITE`, `EXEMPTION_EMERGENCY_LIGHT_OFF_NORMAL_BUSINESS_HRS`, `EXEMPTION_MUSEUM_DISPLAY`, `EXEMPTION_SEARCHLIGHTS`, `EXEMPTION_SLEEPING_UNIT`, `EXEMPTION_VISUALLY_IMPAIRED` + +#### `TrackLightingWattageBasisTypeOptions` + +Added: `NONE`, `null` + +#### `AdvancedControlsAllowanceTypeOptions` + +Added: `null`. Changed `type` from `"string"` to `["string", "null"]`. + +#### `WholeBuildingTypeOptions` + +Added: `WHOLE_BUILDING_INVALID_USE` + +#### `ExteriorLightingZoneTypeOptions` + +Added: `EXT_ZONE_UNDEVELOPED` + +#### `ThermalBridgeComplianceTypeOptions` + +Fixed typo: `" THERMAL_BRIDGE_AS_DESIGNED"` (leading spaces) corrected to `"THERMAL_BRIDGE_AS_DESIGNED"`. Added `null`. Dropped explicit `"type": "string"` constraint. + +#### `CondenserTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `EconomizerTypeOptions` + +Added: `FLUID_ECONOMIZER` + +#### `FuelTypeOptions` + +Added: `OIL_RESIDUAL`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `BoilerDraftTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `ChillerTypeOptions` + +Added: `CENTRIFUGAL_NON_STANDARD`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `CoolingPlantTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `HeatingPlantTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `HeatPumpChillerHeatingSourceConditionOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `HeatPumpChillerLeavingHeatingWaterTempOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `HeatPumpChillerTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `HeatRejectionTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `FanSystemComplianceMethodOptions` + +Added: `null` + +#### `FanEfficiencyExceptionTypeOptions` + +Added: `NONE`, `null` + +#### `SWHSystemDrawPatternTypeOptions` + +Added: `NO_COOLING_EQUIPMENT`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `SWHFuelTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `EquipmentEfficiencyRequirementExceptionOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `RenewableExceptionOptions` + +Added: `RENEWABLE_ONSITE_EXCEPTION_IECC2024_LOW_FLOOR_AREA`, `null`. Dropped explicit `"type": "string"` constraint. + +#### `BallastTypeOptions` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `RequirementAnswerStatus` + +Added: `null`. Dropped explicit `"type": "string"` constraint. + +#### `CompliancePathOptions` + +Added: `COMPLIANCE_PATH_A`, `COMPLIANCE_PATH_B`, `COMPLIANCE_PATH_UNKNOWN` + +#### `ActivityTypeOptions` + +Added: `ACTIVITY_COMMON_CONFERENCE_CELL`, `ACTIVITY_COMMON_GUESTROOM`, `ACTIVITY_COMMON_PATIENT`, `ACTIVITY_COMMON_WELLNESS_LOUNGE`, `ACTIVITY_GAME_HIGH_LIMITS_GAME`, `ACTIVITY_GAME_SLOTS`, `ACTIVITY_GAME_SPORTSBOOK`, `ACTIVITY_GAME_TABLE_GAMES`, `ACTIVITY_HOSPITAL_TELEMEDICINE_ROOM`, `ACTIVITY_PARKING_DAYLIGHT_TRANSITION_ZONE`, `ACTIVITY_RETAIL_MASSAGE_SPACE`, `ACTIVITY_RETAIL_NAIL_SALON`, `ACTIVITY_RETAIL_NAIL_SALON_MALL`, `ACTIVITY_RETAIL_HAIR_SALON`, `ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_FACILITIES`, `ACTIVITY_SECURITY_SCREEN_TRANSPORTATION_WAIT_AREA`, `ACTIVITY_TRANS_AIRPORT_HANGER`, `ACTIVITY_TRANS_PASSENGER_LOAD`, `ACTIVITY_SECURITY_SCREEN_GENERAL_AREA`, `ACTIVITY_SPORTS_POOL_CLASS1`, `ACTIVITY_SPORTS_POOL_CLASS2`, `ACTIVITY_SPORTS_POOL_CLASS3`, `ACTIVITY_SPORTS_POOL_CLASS4` + +Removed: `ACTIVITY_COMMON_OFFICE` + +--- + +### 8. Default Value Changes + +| Definition | Field | Old default | New default | +|---|---|---|---| +| `AgWall` | `continuousDeratedRValue` | `0.0` | *(removed)* | +| `Window` | `preAltPropUval` | `0.0` | *(removed)* | +| `Door` | `preAltPropUval` | `0.0` | *(removed)* | +| `Skylight` | `preAltPropUval` | `0.0` | *(removed)* | +| `HVACPlant` | `efficiencyRequirementException` | `"EFF_EXCEPTION_UNSPECIFIED"` | *(removed)* | +| `ServiceWaterHeatingSystem` | `swhSystemSubType` | `"UNKNOWN_SWH_SYSTEM_SUB_TYPE"` | *(removed)* | +| `ServiceWaterHeatingSystem` | `efficiencyRequirementException` | `"EFF_EXCEPTION_UNSPECIFIED"` | *(removed)* | +| `ServiceWaterHeatingSystem` | `heatPumpPoolHeater` (renamed) | *(none)* | `null` | +| `ServiceWaterHeatingSystem` | `circulationPump` | *(none)* | `0` | +| `ServiceWaterHeatingSystem` | `heatTraceTapeInstalled` | *(none)* | `0` | +| `ServiceWaterHeatingSystem` | `combinedSystem` | *(none)* | `0` | +| `ServiceWaterHeatingSystem` | `poolSystem` | *(none)* | `0` | +| `InteriorLightingFixture` | `advControlsAllowanceType` | *(none)* | `null` | +| `HVAC` | `fanSystem` | *(none)* | `null` | + +--- + +### 9. Miscellaneous / Formatting-only + +A large portion of the diff consists of converting compact inline JSON arrays like `["string", "null"]` into multi-line form. These are cosmetic changes with **no semantic impact** on validation. + +The schema `version` field at the root was bumped from `"0.0.1"` to `"0.0.2"`. + +--- + +### 10. Post-merge corrections + +After review, the boolean → integer changes in PR #25 were reverted. The backend treats these fields as booleans semantically (`0 = false`, `1 = true`), and the server accepts and returns `true`/`false`. Modeling them as `integer enum [0, 1]` required a runtime `IntEnum → bool` conversion workaround in `CustomBaseModel.model_dump` and introduced unnecessary `IntEnum` wrapper classes in `core_types.py`. All nine fields were restored to their original `boolean` types. + +| Definition | Field | PR #25 change | Reverted to | +|---|---|---|---| +| `ComBuilding` | `isHistoricBuilding` | `integer enum [0, 1]` | `boolean`, default `false` | +| `HVACSystem` | `heatRecovery` | `integer enum [0, 1, null]` | `["boolean", "null"]` | +| `HVACSystem` | `heatPumpSimultaneousCoolingAndHeating` | `integer enum [0, 1, null]` | `["boolean", "null"]` | +| `HVACPlant` | `twoPipeSystem` | `integer enum [0, 1, null]` | `["boolean", "null"]` | +| `HVACPlant` | `waterloopHeatPump` | `integer enum [0, 1, null]` | `["boolean", "null"]` | +| `ServiceWaterHeatingSystem` | `circulationPump` | `integer enum [0, 1]`, default `0` | `boolean`, default `false` | +| `ServiceWaterHeatingSystem` | `heatTraceTapeInstalled` | `integer enum [0, 1]`, default `0` | `boolean`, default `false` | +| `ServiceWaterHeatingSystem` | `combinedSystem` | `integer enum [0, 1]`, default `0` | `boolean`, default `false` | +| `ServiceWaterHeatingSystem` | `poolSystem` | `integer enum [0, 1]`, default `0` | `boolean`, default `false` | + +The `IsHistoricBuilding`, `CirculationPump`, `HeatTraceTapeInstalled`, `CombinedSystem`, and `PoolSystem` `IntEnum` classes were removed from `core_types.py` as a result. diff --git a/docs_site/api/operations/exterior-lighting.md b/docs_site/api/operations/exterior-lighting.md index 3989cd9..84b0999 100644 --- a/docs_site/api/operations/exterior-lighting.md +++ b/docs_site/api/operations/exterior-lighting.md @@ -1,12 +1,138 @@ -# Exterior Lighting +# Exterior Lighting Operations -!!! warning "Under Development" - Exterior lighting operations are not yet implemented. This section is planned for a future release. +Exterior lighting is managed at the **`ExteriorUse`** granularity. Each +`ExteriorUse` lives directly under `lighting.exteriorUse[]` (no parent +building area needed) and carries exactly one singleton +`ExteriorLightingSpace` whose `fixture[]` holds the fixtures. -This page will cover operations for managing exterior lighting in COMcheck projects, including: +## Zone type -- Adding and updating exterior lighting use areas -- Managing exterior fixture schedules -- Exterior lighting power allowance calculations +Exterior compliance requires a real zone type on +`lighting.exteriorLightingZoneType`. Set it with +`set_exterior_lighting_zone_type_in_project` **before** exterior compliance +can be evaluated. Adding an `ExteriorUse` while the zone is still +`EXT_ZONE_UNSPECIFIED` emits a `UserWarning` (not an error) so you can build +up a project incrementally. -The underlying types for exterior lighting (`ExteriorUse`, `ExteriorLightingSpace`, `Fixture`, `FixtureSchedule`) are already available. See the [Types Guide](types-guide.md) for details on importing and using them. +## areaDescription uniqueness + +`areaDescription` must be unique within `lighting.exteriorUse[]`. It is the +identifier used by update and remove operations. If missing, a unique value is +auto-generated with the prefix `"Ext Area"`. + +## Operations + +```python +from comcheck_api import project_exterior_lighting_operations as el_ops +``` + +| Function | Description | +|---|---| +| `set_exterior_lighting_zone_type_in_project(project, zone_type)` | Set the project-level exterior lighting zone type (rejects `EXT_ZONE_UNSPECIFIED`) | +| `add_exterior_lighting_area_to_project(project, new_exterior_lighting_area)` | Add a new ExteriorUse | +| `update_exterior_lighting_area_in_project(project, area_description, updates)` | Update an existing ExteriorUse (including its fixtures) | +| `remove_exterior_lighting_area_from_project(project, area_description)` | Remove an ExteriorUse and its fixtures | +| `get_exterior_lighting_area_keys_from_project(project)` | List all exterior uses in the project | + +## Setting the zone type + +```python +from comcheck_api import project_exterior_lighting_operations as el_ops +from comcheck_api.types.core_types import ExteriorLightingZoneTypeOptions + +project = el_ops.set_exterior_lighting_zone_type_in_project( + project, ExteriorLightingZoneTypeOptions.EXT_ZONE_NEIGHBORHOOD_BUS_DISTRICT +) +``` + +`EXT_ZONE_UNSPECIFIED` is rejected with a `ValueError`. A raw string raises +a `TypeError` — always use the enum. + +## Adding an ExteriorUse with fixtures + +```python +from comcheck_api.defaults import get_default_exterior_lighting_area_template, get_default_fixture_template +from comcheck_api.types.core_types import ExteriorUseTypeOptions + +# fixtureType is the required identifier (a description string); lightingType +# is optional and marked for deprecation. +fixture = get_default_fixture_template() +fixture.description = "Parking LED" +fixture.fixtureType = "Parking LED" +fixture.fixtureWattage = 150.0 +fixture.quantity = 8 + +exterior_use = get_default_exterior_lighting_area_template() +exterior_use.areaDescription = "Main Parking Area" +exterior_use.exteriorType = ExteriorUseTypeOptions.EXTERIOR_PARKING_AREA +exterior_use.useQuantity = 5000.0 +exterior_use.exteriorLightingSpace = exterior_use.exteriorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} +) + +project = el_ops.add_exterior_lighting_area_to_project(project, exterior_use) +``` + +## Updating an ExteriorUse + +Pass only the fields you want to change — unchanged fields (including +fixtures) are preserved: + +```python +project = el_ops.update_exterior_lighting_area_in_project( + project, + "Main Parking Area", + {"useQuantity": 6000.0}, +) +``` + +## Adding a fixture to an existing ExteriorUse + +Retrieve the current `exteriorLightingSpace`, append to its `fixture[]`, then +pass it back through `update_exterior_lighting_area_in_project`: + +```python +exterior_uses = project.get_by_path("lighting.exteriorUse") +eu = next(e for e in exterior_uses if e.areaDescription == "Main Parking Area") +existing_fixtures = list(eu.exteriorLightingSpace.fixture or []) + +new_fixture = get_default_fixture_template() +new_fixture.description = "Entrance LED" +new_fixture.fixtureWattage = 80.0 + +updated_space = eu.exteriorLightingSpace.model_copy( + deep=True, + update={"fixture": existing_fixtures + [new_fixture]}, +) +project = el_ops.update_exterior_lighting_area_in_project( + project, + "Main Parking Area", + {"exteriorLightingSpace": updated_space.model_dump(mode="python")}, +) +``` + +## Removing a fixture + +Pass `exteriorLightingSpace` with the desired `fixture[]` (omit the fixtures +you want to remove): + +```python +project = el_ops.update_exterior_lighting_area_in_project( + project, + "Main Parking Area", + {"exteriorLightingSpace": {"fixture": []}}, # removes all fixtures +) +``` + +## Removing an ExteriorUse + +```python +project = el_ops.remove_exterior_lighting_area_from_project(project, "Main Parking Area") +``` + +## Listing exterior uses + +```python +keys = el_ops.get_exterior_lighting_area_keys_from_project(project) +# [{"areaDescription": "Main Parking Area", "exteriorType": "EXTERIOR_PARKING_AREA"}, ...] +``` diff --git a/docs_site/api/operations/interior-lighting.md b/docs_site/api/operations/interior-lighting.md index 69301e5..4faf13d 100644 --- a/docs_site/api/operations/interior-lighting.md +++ b/docs_site/api/operations/interior-lighting.md @@ -1,13 +1,138 @@ -# Interior Lighting +# Interior Lighting Operations -!!! warning "Under Development" - Interior lighting operations are not yet implemented. This section is planned for a future release. +Interior lighting is managed at the **`ActivityUse`** granularity. Each +`ActivityUse` belongs to a `WholeBldgUse` (building area) and carries exactly +one singleton `InteriorLightingSpace` whose `fixture[]` holds the fixtures. -This page will cover operations for managing interior lighting in COMcheck projects, including: +## Key concepts -- Adding and updating whole building use areas with lighting data -- Managing activity use areas and fixture schedules -- Lighting power allowance calculations -- Lighting controls +- **No fixture-level operations.** To add, change, or remove a fixture, edit + the `activityUse`'s `interiorLightingSpace.fixture[]` list and pass the whole + `ActivityUse` through `update_interior_lighting_space_in_project`. +- **ActivityUse.key** is always set to the parent `WholeBldgUse.key` — the + add operation sets this automatically. +- **`areaDescription` must be unique** within a building area's `activityUse[]` + list. It is the identifier used by update and remove operations. If missing, + a unique value is auto-generated with the prefix `"Space"`. +- A building area must exist before adding activity uses — add one with + `project_building_area_operations.add_building_area_to_project` first. -The underlying types for interior lighting (`WholeBldgUse`, `ActivityUse`, `Fixture`, `FixtureSchedule`) are already available. See the [Types Guide](types-guide.md) for details on importing and using them. +## Operations + +```python +from comcheck_api import project_interior_lighting_operations as il_ops +``` + +| Function | Description | +|---|---| +| `add_interior_lighting_space_to_project(project, building_area_key, new_interior_lighting_space)` | Add a new ActivityUse to a building area | +| `update_interior_lighting_space_in_project(project, building_area_key, area_description, updates)` | Update an existing ActivityUse (including its fixtures) | +| `remove_interior_lighting_space_from_project(project, building_area_key, area_description)` | Remove an ActivityUse and its fixtures | +| `get_interior_lighting_space_keys_from_project(project, building_area_key)` | List all activity uses in a building area | + +## Adding an ActivityUse with fixtures + +```python +from comcheck_api import ( + project_building_area_operations as ba_ops, + project_interior_lighting_operations as il_ops, +) +from comcheck_api.defaults import ( + get_default_building_area_template, + get_default_interior_lighting_space_template, + get_default_fixture_template, +) +from comcheck_api.types.core_types import ActivityTypeOptions + +# A building area must exist first +area = get_default_building_area_template() +project = ba_ops.add_building_area_to_project(project, area) +area_key = area.key + +# Build the fixture. fixtureType is the required identifier (a description +# string); lightingType is optional and marked for deprecation. +fixture = get_default_fixture_template() +fixture.description = "Recessed LED" +fixture.fixtureType = "Recessed LED" +fixture.fixtureWattage = 20.0 +fixture.quantity = 10 + +# Attach the fixture to the activity use before adding +activity_use = get_default_interior_lighting_space_template() +activity_use.areaDescription = "Open Office" +activity_use.activityType = ActivityTypeOptions.ACTIVITY_COMMON_OFFICE_OPEN +activity_use.floorArea = 2000.0 +activity_use.interiorLightingSpace = activity_use.interiorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} +) + +project = il_ops.add_interior_lighting_space_to_project(project, area_key, activity_use) +``` + +## Updating an ActivityUse + +Pass only the fields you want to change — unchanged fields (including fixtures) +are preserved: + +```python +project = il_ops.update_interior_lighting_space_in_project( + project, + area_key, + "Open Office", + {"floorArea": 2500.0}, +) +``` + +## Adding a fixture to an existing ActivityUse + +Retrieve the current `interiorLightingSpace`, append to its `fixture[]`, then +pass it back through `update_interior_lighting_space_in_project`: + +```python +whole_use = project.get_by_path("lighting.wholeBldgUse") +ba = next(a for a in whole_use if a.key == area_key) +au = next(au for au in ba.activityUse if au.areaDescription == "Open Office") +existing_fixtures = list(au.interiorLightingSpace.fixture or []) + +new_fixture = get_default_fixture_template() +new_fixture.description = "Pendant LED" +new_fixture.fixtureWattage = 35.0 + +updated_space = au.interiorLightingSpace.model_copy( + deep=True, + update={"fixture": existing_fixtures + [new_fixture]}, +) +project = il_ops.update_interior_lighting_space_in_project( + project, + area_key, + "Open Office", + {"interiorLightingSpace": updated_space.model_dump(mode="python")}, +) +``` + +## Removing a fixture + +Pass `interiorLightingSpace` with the desired `fixture[]` (simply omit the +fixture you want to remove): + +```python +project = il_ops.update_interior_lighting_space_in_project( + project, + area_key, + "Open Office", + {"interiorLightingSpace": {"fixture": []}}, # removes all fixtures +) +``` + +## Removing an ActivityUse + +```python +project = il_ops.remove_interior_lighting_space_from_project(project, area_key, "Open Office") +``` + +## Listing activity uses + +```python +keys = il_ops.get_interior_lighting_space_keys_from_project(project, area_key) +# [{"areaDescription": "Open Office", "activityType": "ACTIVITY_COMMON_OFFICE_OPEN"}, ...] +``` diff --git a/docs_site/getting-started.md b/docs_site/getting-started.md index acf35c4..68d8dd8 100644 --- a/docs_site/getting-started.md +++ b/docs_site/getting-started.md @@ -1,7 +1,7 @@ # Getting Started !!! note "Supported Sections" - Currently, only **Building Area**, **Envelope**, and **Compliance Simulation** operations are fully supported. Interior lighting, exterior lighting, mechanical, credits, and renewable energy sections are planned but not yet implemented. See the [home page](index.md#current-status) for the full status table. + Currently, **Building Area**, **Envelope**, **Interior Lighting**, **Exterior Lighting**, and **Compliance Simulation** operations are fully supported. Mechanical, credits, and renewable energy sections are planned but not yet implemented. See the [home page](index.md#current-status) for the full status table. ## Setup @@ -105,6 +105,7 @@ client.update_project("project-id", project) ```python import time from comcheck_api import COMcheckClient +from comcheck_api.types import SimulationStatus client = COMcheckClient(api_key="your-key") project = client.get_project("project-id") @@ -112,12 +113,15 @@ project = client.get_project("project-id") # Start simulation session_id = client.start_run_simulation(project) -# Poll for completion +# Poll until terminal — only SUCCESS and FAILED are guaranteed terminal states. +# Don't poll faster than every 5 seconds. while True: status = client.get_simulation_status(session_id) - if status["status"] == "COMPLETED": + if status["status"] == SimulationStatus.SUCCESS: break - time.sleep(2) + if status["status"] == SimulationStatus.FAILED: + raise RuntimeError(f"Simulation failed: {status.get('message')}") + time.sleep(5) # Get results result = client.get_simulation_result(session_id) diff --git a/docs_site/index.md b/docs_site/index.md index 05ed169..b5623a8 100644 --- a/docs_site/index.md +++ b/docs_site/index.md @@ -10,9 +10,9 @@ This package is under active development. Here is the current support status for |---|---| | Building Area | Supported | | Envelope (roofs, walls, floors, windows, doors, skylights, thermal bridges) | Supported | +| Interior Lighting | Supported | +| Exterior Lighting | Supported | | Compliance Simulation | Supported | -| Interior Lighting | TBD | -| Exterior Lighting | TBD | | Mechanical | TBD | | Credits | TBD | | Renewable Energy | TBD | @@ -23,6 +23,7 @@ Operations and data managers for the TBD sections are not yet implemented. The u - **Type-safe** --- Pydantic models for all API inputs and outputs - **Envelope management** --- Roofs, walls, floors, windows, doors, skylights, and thermal bridges +- **Lighting management** --- Interior lighting (activity uses + fixtures) and exterior lighting (uses, zone type + fixtures) - **Compliance simulation** --- Start simulations and retrieve results programmatically - **Validation** --- JSON schema validation and Pydantic type checking at every boundary @@ -65,4 +66,4 @@ uv add comcheck_api | Module | Description | |--------|-------------| | [`comcheck_api.client`](api/client.md) | High-level client interface | -| [`comcheck_api.project_operations`](api/operations/building-area.md) | [Building area](api/operations/building-area.md) and [envelope](api/operations/envelope.md) operations | +| [`comcheck_api.project_operations`](api/operations/building-area.md) | [Building area](api/operations/building-area.md), [envelope](api/operations/envelope.md), [interior lighting](api/operations/interior-lighting.md), and [exterior lighting](api/operations/exterior-lighting.md) operations | diff --git a/examples/README.md b/examples/README.md index 36d6221..c780135 100644 --- a/examples/README.md +++ b/examples/README.md @@ -82,6 +82,34 @@ python examples/project_operations/building_area_operations.py - `testProjectJson/buildingAreaAddedProject.json` - `testProjectJson/buildingAreaUpdatedProject.json` +#### Interior Lighting Operations (`project_operations/interior_lighting_operations.py`) +Demonstrates adding and managing interior lighting (ActivityUse) in a project. + +**What it demonstrates:** +- `add_interior_lighting_space_to_project()` - Adding an ActivityUse with fixtures pre-populated +- `update_interior_lighting_space_in_project()` - Updating fields and adding/removing fixtures +- `remove_interior_lighting_space_from_project()` - Removing an ActivityUse and its fixtures +- `get_interior_lighting_space_keys_from_project()` - Listing activity uses in a building area + +**Usage:** +```bash +python examples/project_operations/interior_lighting_operations.py +``` + +#### Exterior Lighting Operations (`project_operations/exterior_lighting_operations.py`) +Demonstrates setting the exterior lighting zone type and managing ExteriorUse items with fixtures. + +**What it demonstrates:** +- `set_exterior_lighting_zone_type_in_project()` - Setting the project-level zone type +- `add_exterior_lighting_area_to_project()` - Adding an ExteriorUse with fixtures pre-populated +- `update_exterior_lighting_area_in_project()` - Updating fields and adding/removing fixtures +- `remove_exterior_lighting_area_from_project()` - Removing an ExteriorUse and its fixtures + +**Usage:** +```bash +python examples/project_operations/exterior_lighting_operations.py +``` + #### Envelope Operations (`project_operations/envelope_operations.py`) Comprehensive examples for all envelope operations including assemblies and nested components. This script demonstrates the complete workflow of adding and updating envelope components in a COMcheck project. diff --git a/examples/client/assemblies.py b/examples/client/assemblies.py index ba8f07c..9d0538d 100644 --- a/examples/client/assemblies.py +++ b/examples/client/assemblies.py @@ -18,7 +18,7 @@ from comcheck_api.types.core_types import EnergyCodeOptions # Initialize client -load_dotenv() +load_dotenv(override=True) client = COMcheckClient() api_key = os.getenv("COM_API_KEY") or "your-api-key-here" client.set_api_key(api_key) diff --git a/examples/client/compliance_and_report.py b/examples/client/compliance_and_report.py index 59fb05f..431974a 100644 --- a/examples/client/compliance_and_report.py +++ b/examples/client/compliance_and_report.py @@ -28,7 +28,7 @@ from comcheck_api.types import SimulationStatus # Initialize client -load_dotenv() +load_dotenv(override=True) client = COMcheckClient() api_key = os.getenv("COM_API_KEY") or "your-api-key-here" client.set_api_key(api_key) diff --git a/examples/client/simulation.py b/examples/client/simulation.py index 5631307..860ffd0 100644 --- a/examples/client/simulation.py +++ b/examples/client/simulation.py @@ -7,7 +7,7 @@ from comcheck_api.types.core_types import EnergyCodeOptions # Initialize client -load_dotenv() +load_dotenv(override=True) client = COMcheckClient() api_key = os.getenv("COM_API_KEY") or "your-api-key-here" client.set_api_key(api_key) diff --git a/examples/client/user_functions.py b/examples/client/user_functions.py index 355658f..26f3ed8 100644 --- a/examples/client/user_functions.py +++ b/examples/client/user_functions.py @@ -6,7 +6,7 @@ from comcheck_api.defaults import get_default_project_template # Initialize client -load_dotenv() +load_dotenv(override=True) client = COMcheckClient() api_key = os.getenv("COM_API_KEY") or "your-api-key-here" client.set_api_key(api_key) @@ -26,7 +26,7 @@ project_id = projects[0]["_id"] project = client.get_project(project_id) print(f"\nProject {project_id} details:") - print(f"Name: {getattr(project, 'projectName', 'N/A')}") + print(f"Project Title: {getattr(project.project, 'projectTitle', 'N/A')}") print(f"Type: {getattr(project, 'projectType', 'N/A')}") # Example 4: Update a project with default template diff --git a/examples/project_operations/building_area_operations.py b/examples/project_operations/building_area_operations.py index 6397a20..9cb2d28 100644 --- a/examples/project_operations/building_area_operations.py +++ b/examples/project_operations/building_area_operations.py @@ -9,7 +9,7 @@ from comcheck_api.utilities.common import export_to_json # Initialize client -load_dotenv() +load_dotenv(override=True) client = COMcheckClient() api_key = os.getenv("COM_API_KEY") or "your-api-key-here" client.set_api_key(api_key) diff --git a/examples/project_operations/envelope_operations.py b/examples/project_operations/envelope_operations.py index b4eafb4..dd5af44 100644 --- a/examples/project_operations/envelope_operations.py +++ b/examples/project_operations/envelope_operations.py @@ -12,7 +12,7 @@ from comcheck_api.types.core_types import Roof, ThermalBridgeTypeOptions # Initialize client -load_dotenv() +load_dotenv(override=True) api_key = os.getenv("COM_API_KEY") if not api_key: raise ValueError("COM_API_KEY environment variable is not set") diff --git a/examples/project_operations/exterior_lighting_operations.py b/examples/project_operations/exterior_lighting_operations.py new file mode 100644 index 0000000..c7b65ba --- /dev/null +++ b/examples/project_operations/exterior_lighting_operations.py @@ -0,0 +1,152 @@ +"""Example: exterior lighting operations. + +Exterior lighting is managed at the ExteriorUse granularity. Each ExteriorUse +lives directly under lighting.exteriorUse[] (no parent building area needed) +and carries exactly one ExteriorLightingSpace whose fixture[] holds the +fixtures. + +Zone type +--------- +Before exterior compliance can be evaluated, set a real exterior lighting zone +type on the project. Adding an ExteriorUse while the zone is still +EXT_ZONE_UNSPECIFIED emits a warning — call +set_exterior_lighting_zone_type_in_project to fix it. +""" + +import logging +import os +from dotenv import load_dotenv + +from comcheck_api import ( + COMcheckClient, + project_exterior_lighting_operations as el_ops, +) + +# The library logs API failures via logging.getLogger(__name__) but never +# configures a handler (as a library shouldn't). Configure logging here so +# those error logs — including the server's response body — are visible. +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(name)s %(levelname)s %(message)s", +) +from comcheck_api.defaults import ( + get_default_exterior_lighting_area_template, + get_default_fixture_template, +) +from comcheck_api.types.core_types import ( + ExteriorLightingZoneTypeOptions, + ExteriorUseTypeOptions, +) + +load_dotenv(override=True) +client = COMcheckClient() +client.set_api_key(os.getenv("COM_API_KEY") or "your-api-key-here") + +# Fetch an existing project so changes can be saved back to the account. +# (update_project persists to the server; it requires a project that already +# exists there, so we start from a fetched project rather than a local +# template.) +project = client.get_project("your-project-id") +if not project: + raise ValueError("Project not found") +project_id = str(project.id) + + +# ── Step 1: Set the exterior lighting zone type ─────────────────────────────── +# Must be set to a real zone before exterior compliance can be evaluated. +project = el_ops.set_exterior_lighting_zone_type_in_project( + project, ExteriorLightingZoneTypeOptions.EXT_ZONE_NEIGHBORHOOD_BUS_DISTRICT +) + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print(f"Zone type set: {project.lighting.exteriorLightingZoneType}") + +# ── Step 2: Add an ExteriorUse with a fixture already populated ─────────────── +fixture = get_default_fixture_template() +fixture.description = "Parking LED" +# fixtureType is the required identifier (a description string). lightingType +# is optional and marked for deprecation, so it is left unset here. +fixture.fixtureType = "Parking LED" +fixture.fixtureWattage = 150.0 +fixture.quantity = 8 + +exterior_use = get_default_exterior_lighting_area_template() +exterior_use.areaDescription = "Main Parking Area" +exterior_use.exteriorType = ExteriorUseTypeOptions.EXTERIOR_PARKING_AREA +exterior_use.useQuantity = 5000.0 +exterior_use.quantityUnits = "sq ft" +exterior_use.exteriorLightingSpace = exterior_use.exteriorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} +) + +project = el_ops.add_exterior_lighting_area_to_project(project, exterior_use) + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print(f"ExteriorUse added: {exterior_use.areaDescription!r}") + +# ── Step 3: List all exterior uses ──────────────────────────────────────────── +keys = el_ops.get_exterior_lighting_area_keys_from_project(project) +print(f"Exterior uses: {keys}") + +# ── Step 4: Update the ExteriorUse (change quantity) ───────────────────────── +project = el_ops.update_exterior_lighting_area_in_project( + project, + "Main Parking Area", + {"useQuantity": 6000.0}, +) + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print("ExteriorUse updated: useQuantity → 6000.0") + +# ── Step 5: Add a second fixture by updating the lighting space ─────────────── +# Use direct attribute access (not get_by_path, which returns Any) so +# `exterior_use` keeps its real type for editor autocomplete and type checking. +if not project.lighting or not project.lighting.exteriorUse: + raise ValueError("Project has no exterior uses (exteriorUse)") +exterior_use = next( + exterior_use + for exterior_use in project.lighting.exteriorUse + if exterior_use.areaDescription == "Main Parking Area" +) +if exterior_use.exteriorLightingSpace is None: + raise ValueError("ExteriorUse has no exteriorLightingSpace") +existing_fixtures = list(exterior_use.exteriorLightingSpace.fixture or []) + +new_fixture = get_default_fixture_template() +new_fixture.description = "Entrance LED" +new_fixture.fixtureWattage = 80.0 +new_fixture.quantity = 2 + +updated_space = exterior_use.exteriorLightingSpace.model_copy( + deep=True, + update={"fixture": existing_fixtures + [new_fixture]}, +) +project = el_ops.update_exterior_lighting_area_in_project( + project, + "Main Parking Area", + {"exteriorLightingSpace": updated_space.model_dump(mode="python")}, +) + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print("Second fixture added to Main Parking Area") + +# ── Step 6: Remove the ExteriorUse ─────────────────────────────────────────── +project = el_ops.remove_exterior_lighting_area_from_project( + project, "Main Parking Area" +) + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print("ExteriorUse removed: 'Main Parking Area'") + +keys = el_ops.get_exterior_lighting_area_keys_from_project(project) +print(f"Remaining exterior uses: {keys}") diff --git a/examples/project_operations/interior_lighting_operations.py b/examples/project_operations/interior_lighting_operations.py new file mode 100644 index 0000000..6bba1a3 --- /dev/null +++ b/examples/project_operations/interior_lighting_operations.py @@ -0,0 +1,150 @@ +"""Example: interior lighting operations. + +Interior lighting is managed at the ActivityUse granularity. Each ActivityUse +belongs to a WholeBldgUse (building area) and carries exactly one +InteriorLightingSpace whose fixture[] holds the fixtures. + +There are no fixture-level operations — to add, change, or remove a fixture, +edit the activityUse's interiorLightingSpace.fixture[] list and pass the whole +ActivityUse through update_interior_lighting_space_in_project. +""" + +import logging +import os +from dotenv import load_dotenv + +from comcheck_api import ( + COMcheckClient, + project_building_area_operations as ba_ops, + project_interior_lighting_operations as il_ops, +) + +from comcheck_api.defaults import ( + get_default_interior_lighting_space_template, + get_default_building_area_template, + get_default_fixture_template, +) +from comcheck_api.types.core_types import ActivityTypeOptions +from comcheck_api.utilities.common import export_to_json + +load_dotenv(override=True) +client = COMcheckClient() +client.set_api_key(os.getenv("COM_API_KEY") or "your-api-key-here") + +# Fetch an existing project so changes can be saved back to the account. +# (update_project persists to the server; it requires a project that already +# exists there, so we start from a fetched project rather than a local +# template.) +project = client.get_project("your-project-id") +if not project: + raise ValueError("Project not found") +project_id = str(project.id) +export_to_json(project, "interior_lighting_operations_before.json") + + +# ── Step 1: A building area must exist before adding activity uses ──────────── +area = get_default_building_area_template() +area.areaDescription = "Main Office 1" +project = ba_ops.add_building_area_to_project(project, area) +area_key = area.key +export_to_json(project, "interior_lighting_operations_after_add_building_area.json") +# Persist the new building area to the account. + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print(f"Building area added: {area.areaDescription!r} (key={area_key})") + +# ── Step 2: Add an ActivityUse with a fixture already populated ─────────────── +fixture = get_default_fixture_template() +fixture.description = "Recessed LED" +# fixtureType is the required identifier (a description string). lightingType +# is optional and marked for deprecation, so it is left unset here. +fixture.fixtureType = "Recessed LED" +fixture.fixtureWattage = 20.0 +fixture.quantity = 10 + +activity_use = get_default_interior_lighting_space_template() +activity_use.areaDescription = "Open Office" +# Todo: check if activityType options are based on energy code, or if they are just generic options. +activity_use.activityType = ActivityTypeOptions.ACTIVITY_COMMON_OFFICE_OPEN +activity_use.floorArea = 2000.0 +activity_use.interiorLightingSpace = activity_use.interiorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} +) + +project = il_ops.add_interior_lighting_space_to_project(project, area_key, activity_use) +export_to_json(project, "interior_lighting_operations_after_add.json") + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print(f"ActivityUse added: {activity_use.areaDescription!r}") + +# ── Step 3: List all activity uses in the building area ─────────────────────── +keys = il_ops.get_interior_lighting_space_keys_from_project(project, area_key) +print(f"Activity uses in {area.areaDescription!r}: {keys}") + +# ── Step 4: Update the ActivityUse (change floor area) ─────────────────────── +project = il_ops.update_interior_lighting_space_in_project( + project, + area_key, + "Open Office", + {"floorArea": 2500.0}, +) + +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print("ActivityUse updated: floorArea → 2500.0") + +# ── Step 5: Add a second fixture by updating the lighting space ─────────────── +# Retrieve current activityUse to get the existing fixtures. +# Use direct attribute access (not get_by_path, which returns Any) so +# `building_area`, `activity_use`, etc. keep their real types for editor +# autocomplete and type checking. +if not project.lighting or not project.lighting.wholeBldgUse: + raise ValueError("Project has no building areas (wholeBldgUse)") +building_area = next( + area for area in project.lighting.wholeBldgUse if area.key == area_key +) +activity_use = next( + activity_use + for activity_use in building_area.activityUse + if activity_use.areaDescription == "Open Office" +) +if activity_use.interiorLightingSpace is None: + raise ValueError("ActivityUse has no interiorLightingSpace") +existing_fixtures = list(activity_use.interiorLightingSpace.fixture or []) + +new_fixture = get_default_fixture_template() +new_fixture.description = "Pendant LED" +new_fixture.fixtureWattage = 35.0 +new_fixture.quantity = 4 + +updated_space = activity_use.interiorLightingSpace.model_copy( + deep=True, + update={"fixture": existing_fixtures + [new_fixture]}, +) +project = il_ops.update_interior_lighting_space_in_project( + project, + area_key, + "Open Office", + {"interiorLightingSpace": updated_space.model_dump(mode="python")}, +) +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print("Second fixture added to Open Office") + +# ── Step 6: Remove the ActivityUse ─────────────────────────────────────────── +project = il_ops.remove_interior_lighting_space_from_project( + project, area_key, "Open Office" +) +project = client.update_project(project_id, project) +if not project: + raise ValueError("Project not found after update") +print("ActivityUse removed: 'Open Office'") + +keys = il_ops.get_interior_lighting_space_keys_from_project(project, area_key) +print(f"Remaining activity uses: {keys}") diff --git a/pyproject.toml b/pyproject.toml index 57e1a55..1b153b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ keywords = [ dependencies = [ "httpx>=0.27.0", + "jsondiff>=2.2.1", "jsonschema>=4.23.0", "pydantic>=2.12.5", "python-dotenv>=1.0.0", @@ -60,7 +61,7 @@ members = [] [dependency-groups] dev = [ "black>=26.1.0", - "datamodel-code-generator>=0.54.1", + "datamodel-code-generator>=0.72.3", "mypy>=1.19.1", "pre-commit>=4.5.1", "pytest>=9.0.2", diff --git a/tests/project_operation_tests/test_building_area_operations.py b/tests/project_operation_tests/test_building_area_operations.py index 7964fe6..ae9130f 100644 --- a/tests/project_operation_tests/test_building_area_operations.py +++ b/tests/project_operation_tests/test_building_area_operations.py @@ -1,3 +1,4 @@ +import pytest from copy import deepcopy from comcheck_api.client import COMcheckClient @@ -38,6 +39,34 @@ def test_building_area_operations( ) +def test_add_building_area_duplicate_description_raises(project: ComBuilding): + existing = project.get_by_path("lighting.wholeBldgUse", []) + assert existing, "fixture project must have at least one building area" + duplicate_desc = getattr(existing[0], "areaDescription") + + duplicate = get_default_building_area_template() + duplicate.areaDescription = duplicate_desc + + with pytest.raises(ValueError, match="areaDescription"): + project_building_area_operations.add_building_area_to_project( + project, duplicate + ) + + +def test_update_building_area_duplicate_description_raises(project: ComBuilding): + local = project.model_copy(deep=True) + + area_a = get_default_building_area_template() + area_b = get_default_building_area_template() + local = project_building_area_operations.add_building_area_to_project(local, area_a) + local = project_building_area_operations.add_building_area_to_project(local, area_b) + + with pytest.raises(ValueError, match="areaDescription"): + project_building_area_operations.update_building_area_in_project( + local, area_b.key, {"areaDescription": area_a.areaDescription} + ) + + def test_get_building_area_keys(project: ComBuilding): local_project = project.model_copy(deep=True) local_project.lighting.wholeBldgUse = [ diff --git a/tests/project_operation_tests/test_exterior_lighting_operations.py b/tests/project_operation_tests/test_exterior_lighting_operations.py new file mode 100644 index 0000000..3b36588 --- /dev/null +++ b/tests/project_operation_tests/test_exterior_lighting_operations.py @@ -0,0 +1,280 @@ +"""Tests for project_exterior_lighting_operations.""" + +import pytest + +from comcheck_api.defaults import ( + get_default_exterior_lighting_area_template, + get_default_fixture_template, +) +from comcheck_api.project_operations import ( + project_exterior_lighting_operations as el_ops, +) +from comcheck_api.types.core_types import ( + ComBuilding, + ExteriorLightingZoneTypeOptions, + ExteriorUseTypeOptions, + LightingTypeOptions, +) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _fresh(project: ComBuilding) -> ComBuilding: + return project.model_copy(deep=True) + + +# --------------------------------------------------------------------------- +# set_exterior_lighting_zone_type_in_project +# --------------------------------------------------------------------------- + + +def test_set_zone_type(project: ComBuilding): + result = el_ops.set_exterior_lighting_zone_type_in_project( + project, ExteriorLightingZoneTypeOptions.EXT_ZONE_METRO_COMMERCIAL + ) + assert ( + result.lighting.exteriorLightingZoneType + == ExteriorLightingZoneTypeOptions.EXT_ZONE_METRO_COMMERCIAL + ) + + +def test_set_zone_type_rejects_unspecified(project: ComBuilding): + with pytest.raises(ValueError, match="EXT_ZONE_UNSPECIFIED"): + el_ops.set_exterior_lighting_zone_type_in_project( + project, ExteriorLightingZoneTypeOptions.EXT_ZONE_UNSPECIFIED + ) + + +def test_set_zone_type_rejects_non_enum(project: ComBuilding): + with pytest.raises(TypeError): + el_ops.set_exterior_lighting_zone_type_in_project(project, "EXT_ZONE_RURAL") + + +def test_set_zone_type_does_not_mutate_original(project: ComBuilding): + proj = _fresh(project) + original_zone = proj.lighting.exteriorLightingZoneType + el_ops.set_exterior_lighting_zone_type_in_project( + proj, ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + assert proj.lighting.exteriorLightingZoneType == original_zone + + +# --------------------------------------------------------------------------- +# add_exterior_lighting_area_to_project +# --------------------------------------------------------------------------- + + +def test_add_exterior_use(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = "Parking lot" + + result = el_ops.add_exterior_lighting_area_to_project(proj, eu) + + keys = el_ops.get_exterior_lighting_area_keys_from_project(result) + assert any(k["areaDescription"] == "Parking lot" for k in keys) + + +def test_add_exterior_use_warns_when_zone_unspecified(project: ComBuilding): + proj = _fresh(project) + # Force zone to unspecified directly + proj.lighting.exteriorLightingZoneType = ( + ExteriorLightingZoneTypeOptions.EXT_ZONE_UNSPECIFIED + ) + + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = "Entry" + + with pytest.warns(UserWarning, match="EXT_ZONE_UNSPECIFIED"): + el_ops.add_exterior_lighting_area_to_project(proj, eu) + + +def test_add_exterior_use_does_not_mutate_original(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + original_count = len(proj.lighting.exteriorUse) + eu = get_default_exterior_lighting_area_template() + el_ops.add_exterior_lighting_area_to_project(proj, eu) + assert len(proj.lighting.exteriorUse) == original_count + + +# --------------------------------------------------------------------------- +# update_exterior_lighting_area_in_project +# --------------------------------------------------------------------------- + + +def test_update_exterior_use(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = "Loading dock" + proj = el_ops.add_exterior_lighting_area_to_project(proj, eu) + + result = el_ops.update_exterior_lighting_area_in_project( + proj, + "Loading dock", + { + "useQuantity": 500.0, + "exteriorType": ExteriorUseTypeOptions.EXTERIOR_LOADING_DOCK, + }, + ) + + exterior_uses = result.get_by_path("lighting.exteriorUse") + updated = next(e for e in exterior_uses if e.areaDescription == "Loading dock") + assert updated.useQuantity == 500.0 + assert updated.exteriorType == ExteriorUseTypeOptions.EXTERIOR_LOADING_DOCK + + +def test_update_exterior_use_not_found(project: ComBuilding): + with pytest.raises(ValueError, match="not found"): + el_ops.update_exterior_lighting_area_in_project( + _fresh(project), "Nonexistent", {"useQuantity": 100.0} + ) + + +# --------------------------------------------------------------------------- +# remove_exterior_lighting_area_from_project +# --------------------------------------------------------------------------- + + +def test_remove_exterior_use(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = "Walkway" + proj = el_ops.add_exterior_lighting_area_to_project(proj, eu) + + result = el_ops.remove_exterior_lighting_area_from_project(proj, "Walkway") + + keys = el_ops.get_exterior_lighting_area_keys_from_project(result) + assert not any(k["areaDescription"] == "Walkway" for k in keys) + + +def test_remove_exterior_use_not_found(project: ComBuilding): + with pytest.raises(ValueError, match="not found"): + el_ops.remove_exterior_lighting_area_from_project( + _fresh(project), "Nonexistent" + ) + + +# --------------------------------------------------------------------------- +# Fixture editing via the ExteriorUse payload +# --------------------------------------------------------------------------- + + +def test_add_fixture_via_exterior_use_update(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = "Canopy" + proj = el_ops.add_exterior_lighting_area_to_project(proj, eu) + + fixture = get_default_fixture_template() + fixture.description = "Canopy LED" + fixture.fixtureWattage = 60.0 + + exterior_uses = proj.get_by_path("lighting.exteriorUse") + added_eu = next(e for e in exterior_uses if e.areaDescription == "Canopy") + updated_space = added_eu.exteriorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} + ) + result = el_ops.update_exterior_lighting_area_in_project( + proj, + "Canopy", + { + "exteriorLightingSpace": updated_space.model_dump( + mode="python" + ) + }, + ) + + exterior_uses = result.get_by_path("lighting.exteriorUse") + updated_eu = next(e for e in exterior_uses if e.areaDescription == "Canopy") + fixtures = updated_eu.exteriorLightingSpace.fixture or [] + assert len(fixtures) == 1 + assert fixtures[0].description == "Canopy LED" + assert fixtures[0].fixtureWattage == 60.0 + + +def test_fixture_fields_preserved_on_exterior_use_update(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = "Plaza" + fixture = get_default_fixture_template() + fixture.description = "Plaza LED" + fixture.quantity = 6 + eu.exteriorLightingSpace = eu.exteriorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} + ) + proj = el_ops.add_exterior_lighting_area_to_project(proj, eu) + + # Update only useQuantity — fixtures must be untouched + result = el_ops.update_exterior_lighting_area_in_project( + proj, "Plaza", {"useQuantity": 800.0} + ) + + exterior_uses = result.get_by_path("lighting.exteriorUse") + updated_eu = next(e for e in exterior_uses if e.areaDescription == "Plaza") + fixtures = updated_eu.exteriorLightingSpace.fixture or [] + assert len(fixtures) == 1 + assert fixtures[0].description == "Plaza LED" + assert fixtures[0].quantity == 6 + + +def test_remove_fixture_by_omitting_from_update(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = "Driveway" + fixture = get_default_fixture_template() + fixture.description = "Driveway LED" + eu.exteriorLightingSpace = eu.exteriorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} + ) + proj = el_ops.add_exterior_lighting_area_to_project(proj, eu) + + result = el_ops.update_exterior_lighting_area_in_project( + proj, "Driveway", {"exteriorLightingSpace": {"fixture": []}} + ) + + exterior_uses = result.get_by_path("lighting.exteriorUse") + updated_eu = next(e for e in exterior_uses if e.areaDescription == "Driveway") + assert (updated_eu.exteriorLightingSpace.fixture or []) == [] + + +# --------------------------------------------------------------------------- +# get_exterior_lighting_area_keys_from_project +# --------------------------------------------------------------------------- + + +def test_get_exterior_use_keys_empty(project: ComBuilding): + proj = _fresh(project) + proj.lighting.exteriorUse = [] + result = el_ops.get_exterior_lighting_area_keys_from_project(proj) + assert result == [] + + +def test_get_exterior_use_keys_returns_all(project: ComBuilding): + proj = el_ops.set_exterior_lighting_zone_type_in_project( + _fresh(project), ExteriorLightingZoneTypeOptions.EXT_ZONE_RURAL + ) + for desc in ["Entry A", "Entry B"]: + eu = get_default_exterior_lighting_area_template() + eu.areaDescription = desc + proj = el_ops.add_exterior_lighting_area_to_project(proj, eu) + + keys = el_ops.get_exterior_lighting_area_keys_from_project(proj) + descriptions = [k["areaDescription"] for k in keys] + assert "Entry A" in descriptions + assert "Entry B" in descriptions diff --git a/tests/project_operation_tests/test_interior_lighting_operations.py b/tests/project_operation_tests/test_interior_lighting_operations.py new file mode 100644 index 0000000..5faa5bb --- /dev/null +++ b/tests/project_operation_tests/test_interior_lighting_operations.py @@ -0,0 +1,255 @@ +"""Tests for project_interior_lighting_operations.""" + +import pytest + +from comcheck_api.defaults import ( + get_default_interior_lighting_space_template, + get_default_building_area_template, + get_default_fixture_template, +) +from comcheck_api.project_operations import ( + project_building_area_operations, + project_interior_lighting_operations as il_ops, +) +from comcheck_api.types.core_types import ( + ActivityTypeOptions, + ComBuilding, + LightingTypeOptions, +) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _fresh_project_with_area(project: ComBuilding) -> tuple[ComBuilding, str]: + """Return a deep-copy of project with one new building area added.""" + proj = project.model_copy(deep=True) + area = get_default_building_area_template() + proj = project_building_area_operations.add_building_area_to_project(proj, area) + return proj, area.key + + +# --------------------------------------------------------------------------- +# Lifecycle: add / update / remove +# --------------------------------------------------------------------------- + + +def test_add_activity_use(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + activity_use = get_default_interior_lighting_space_template() + activity_use.areaDescription = "Office space" + + result = il_ops.add_interior_lighting_space_to_project(proj, area_key, activity_use) + + keys = il_ops.get_interior_lighting_space_keys_from_project(result, area_key) + descriptions = [k["areaDescription"] for k in keys] + assert "Office space" in descriptions + + +def test_add_activity_use_sets_key(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + activity_use = get_default_interior_lighting_space_template() + + result = il_ops.add_interior_lighting_space_to_project(proj, area_key, activity_use) + + whole_use = result.get_by_path("lighting.wholeBldgUse") + area = next(a for a in whole_use if a.key == area_key) + added = area.activityUse[-1] + assert added.key == area_key + + +def test_update_activity_use(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + activity_use = get_default_interior_lighting_space_template() + activity_use.areaDescription = "Conference room" + proj = il_ops.add_interior_lighting_space_to_project(proj, area_key, activity_use) + + result = il_ops.update_interior_lighting_space_in_project( + proj, + area_key, + "Conference room", + { + "floorArea": 2500.0, + "activityType": ActivityTypeOptions.ACTIVITY_COMMON_CONFERENCE_HALL, + }, + ) + + whole_use = result.get_by_path("lighting.wholeBldgUse") + area = next(a for a in whole_use if a.key == area_key) + updated_au = next( + au for au in area.activityUse if au.areaDescription == "Conference room" + ) + assert updated_au.floorArea == 2500.0 + assert ( + updated_au.activityType == ActivityTypeOptions.ACTIVITY_COMMON_CONFERENCE_HALL + ) + + +def test_remove_activity_use(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + activity_use = get_default_interior_lighting_space_template() + activity_use.areaDescription = "Storage room" + proj = il_ops.add_interior_lighting_space_to_project(proj, area_key, activity_use) + + result = il_ops.remove_interior_lighting_space_from_project( + proj, area_key, "Storage room" + ) + + keys = il_ops.get_interior_lighting_space_keys_from_project(result, area_key) + assert "Storage room" not in [k["areaDescription"] for k in keys] + + +# --------------------------------------------------------------------------- +# Fixture editing via the ActivityUse payload +# --------------------------------------------------------------------------- + + +def test_add_fixture_via_activity_use_update(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + activity_use = get_default_interior_lighting_space_template() + activity_use.areaDescription = "Lab" + proj = il_ops.add_interior_lighting_space_to_project(proj, area_key, activity_use) + + fixture = get_default_fixture_template() + fixture.description = "Lab LED" + fixture.fixtureWattage = 48.0 + + # Retrieve the current activityUse, append the fixture, then update + whole_use = proj.get_by_path("lighting.wholeBldgUse") + area = next(a for a in whole_use if a.key == area_key) + au = next(au for au in area.activityUse if au.areaDescription == "Lab") + + updated_space = au.interiorLightingSpace.model_copy( + deep=True, + update={"fixture": [fixture]}, + ) + result = il_ops.update_interior_lighting_space_in_project( + proj, + area_key, + "Lab", + { + "interiorLightingSpace": updated_space.model_dump( + mode="python" + ) + }, + ) + + whole_use = result.get_by_path("lighting.wholeBldgUse") + area = next(a for a in whole_use if a.key == area_key) + au = next(au for au in area.activityUse if au.areaDescription == "Lab") + fixtures = au.interiorLightingSpace.fixture or [] + assert len(fixtures) == 1 + assert fixtures[0].description == "Lab LED" + assert fixtures[0].fixtureWattage == 48.0 + + +def test_remove_fixture_by_omitting_from_update(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + activity_use = get_default_interior_lighting_space_template() + activity_use.areaDescription = "Lobby" + fixture = get_default_fixture_template() + fixture.description = "Lobby fixture" + activity_use.interiorLightingSpace = activity_use.interiorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} + ) + proj = il_ops.add_interior_lighting_space_to_project(proj, area_key, activity_use) + + # Update with empty fixture list — effectively removes all fixtures + result = il_ops.update_interior_lighting_space_in_project( + proj, + area_key, + "Lobby", + {"interiorLightingSpace": {"fixture": []}}, + ) + + whole_use = result.get_by_path("lighting.wholeBldgUse") + area = next(a for a in whole_use if a.key == area_key) + au = next(au for au in area.activityUse if au.areaDescription == "Lobby") + assert (au.interiorLightingSpace.fixture or []) == [] + + +def test_fixture_fields_preserved_on_activity_use_update(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + activity_use = get_default_interior_lighting_space_template() + activity_use.areaDescription = "Gym" + fixture = get_default_fixture_template() + fixture.description = "Gym LED" + fixture.lightingType = LightingTypeOptions.LED + fixture.quantity = 4 + activity_use.interiorLightingSpace = activity_use.interiorLightingSpace.model_copy( + deep=True, update={"fixture": [fixture]} + ) + proj = il_ops.add_interior_lighting_space_to_project(proj, area_key, activity_use) + + # Update only the floorArea — fixtures must be untouched + result = il_ops.update_interior_lighting_space_in_project( + proj, area_key, "Gym", {"floorArea": 3000.0} + ) + + whole_use = result.get_by_path("lighting.wholeBldgUse") + area = next(a for a in whole_use if a.key == area_key) + au = next(au for au in area.activityUse if au.areaDescription == "Gym") + fixtures = au.interiorLightingSpace.fixture or [] + assert len(fixtures) == 1 + assert fixtures[0].description == "Gym LED" + assert fixtures[0].quantity == 4 + + +# --------------------------------------------------------------------------- +# Error cases +# --------------------------------------------------------------------------- + + +def test_add_activity_use_invalid_building_area(project: ComBuilding): + with pytest.raises(ValueError, match="not found"): + il_ops.add_interior_lighting_space_to_project( + project, "nonexistent-key", get_default_interior_lighting_space_template() + ) + + +def test_update_activity_use_not_found(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + with pytest.raises(ValueError, match="not found"): + il_ops.update_interior_lighting_space_in_project( + proj, area_key, "Nonexistent space", {"floorArea": 100.0} + ) + + +def test_remove_activity_use_not_found(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + with pytest.raises(ValueError, match="not found"): + il_ops.remove_interior_lighting_space_from_project( + proj, area_key, "Nonexistent space" + ) + + +# --------------------------------------------------------------------------- +# get_interior_lighting_space_keys_from_project +# --------------------------------------------------------------------------- + + +def test_get_activity_use_keys_empty(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + result = il_ops.get_interior_lighting_space_keys_from_project(proj, area_key) + assert result == [] + + +def test_get_activity_use_keys_unknown_area(project: ComBuilding): + result = il_ops.get_interior_lighting_space_keys_from_project( + project, "nonexistent-key" + ) + assert result == [] + + +def test_get_activity_use_keys_returns_all(project: ComBuilding): + proj, area_key = _fresh_project_with_area(project) + for desc in ["Space A", "Space B"]: + au = get_default_interior_lighting_space_template() + au.areaDescription = desc + proj = il_ops.add_interior_lighting_space_to_project(proj, area_key, au) + + keys = il_ops.get_interior_lighting_space_keys_from_project(proj, area_key) + descriptions = [k["areaDescription"] for k in keys] + assert "Space A" in descriptions + assert "Space B" in descriptions diff --git a/tools/generate_core_types.py b/tools/generate_core_types.py index 055915f..f8cab9a 100644 --- a/tools/generate_core_types.py +++ b/tools/generate_core_types.py @@ -14,35 +14,45 @@ # Ensure output directory exists OUTPUT_TYPES.parent.mkdir(parents=True, exist_ok=True) -# Run datamodel-codegen CLI -result = subprocess.run( - [ - "datamodel-codegen", - "--input", - str(INPUT_SCHEMA), - "--input-file-type", - "jsonschema", - "--output", - str(OUTPUT_TYPES), - "--extra-fields", - "ignore", - "--output-model-type", - "pydantic_v2.BaseModel", - "--base-class", - "comcheck_api.types.custom_base_model.CustomBaseModel", - "--target-python-version", - "3.13", - "--use-standard-collections", - "--use-schema-description", - "--use-default", # Use default values from the schema - "--field-constraints", # Generate validation constraints (e.g., max_length, minItems) - "--use-annotated", # Best practice for Pydantic V2 validations - ], - check=False, -) -if result.returncode == 0: - print(f"Generated: {OUTPUT_TYPES}") -else: - print(f"Generation failed with exit code {result.returncode}", file=sys.stderr) - sys.exit(result.returncode) +def main(): + """Main function to run the script.""" + # Run the datamodel-codegen command + result = subprocess.run( + [ + "datamodel-codegen", + "--input", + str(INPUT_SCHEMA), + "--input-file-type", + "jsonschema", + "--output", + str(OUTPUT_TYPES), + "--extra-fields", + "ignore", + "--output-model-type", + "pydantic_v2.BaseModel", + "--base-class", + "comcheck_api.types.custom_base_model.CustomBaseModel", + "--target-python-version", + "3.13", + "--use-standard-collections", + "--use-schema-description", + "--use-missing-sentinel", + "--field-constraints", # Generate validation constraints (e.g., max_length, minItems) + "--use-annotated", # Best practice for Pydantic V2 validations + "--formatters", + "black", + "isort", + ], + check=False, + ) + + if result.returncode == 0: + print(f"Generated: {OUTPUT_TYPES}") + else: + print(f"Generation failed with exit code {result.returncode}", file=sys.stderr) + sys.exit(result.returncode) + + +if __name__ == "__main__": + main() diff --git a/uv.lock b/uv.lock index 1cf6932..dc35bf9 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,8 @@ version = 1 revision = 3 requires-python = ">=3.12" resolution-markers = [ - "python_full_version < '4'", + "python_full_version >= '3.14' and python_full_version < '4'", + "python_full_version < '3.14'", "python_full_version >= '4'", ] @@ -220,6 +221,7 @@ version = "1.0.1" source = { editable = "." } dependencies = [ { name = "httpx" }, + { name = "jsondiff" }, { name = "jsonschema" }, { name = "pydantic" }, { name = "python-dotenv" }, @@ -243,6 +245,7 @@ docs = [ [package.metadata] requires-dist = [ { name = "httpx", specifier = ">=0.27.0" }, + { name = "jsondiff", specifier = ">=2.2.1" }, { name = "jsonschema", specifier = ">=4.23.0" }, { name = "pydantic", specifier = ">=2.12.5" }, { name = "python-dotenv", specifier = ">=1.0.0" }, @@ -252,7 +255,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "black", specifier = ">=26.1.0" }, - { name = "datamodel-code-generator", specifier = ">=0.54.1" }, + { name = "datamodel-code-generator", specifier = ">=0.72.3" }, { name = "mypy", specifier = ">=1.19.1" }, { name = "pre-commit", specifier = ">=4.5.1" }, { name = "pytest", specifier = ">=9.0.2" }, @@ -265,22 +268,21 @@ docs = [ [[package]] name = "datamodel-code-generator" -version = "0.54.1" +version = "0.72.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, - { name = "black" }, + { name = "black", marker = "sys_platform != 'emscripten'" }, { name = "genson" }, { name = "inflect" }, - { name = "isort" }, + { name = "isort", marker = "sys_platform != 'emscripten'" }, { name = "jinja2" }, - { name = "packaging" }, { name = "pydantic" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/4b/6a63ea00c65402576e05e8cc963349ffe58db07d8c8183ab51488dbfb67a/datamodel_code_generator-0.54.1.tar.gz", hash = "sha256:dd9eb7594f94a8b85d7e410f4d997a443cf7a52a1dcc049fae6cf35660f18803", size = 829716, upload-time = "2026-03-04T04:15:02.582Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/4b/4652f0bb085a564982c3e515a0b02bcee21765ddac967bcf152a70d2af14/datamodel_code_generator-0.72.3.tar.gz", hash = "sha256:a20160de09b76d4a293ccba5a9ee5c341b5365890237ac1ff812f420e4c09f3b", size = 1965678, upload-time = "2026-08-10T18:58:41.636Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/ce/8a8aadbb2fb428109949d0f7a42232d1d452dab0b8550f6e8c5843afa93d/datamodel_code_generator-0.54.1-py3-none-any.whl", hash = "sha256:67c59ff2368eb2ec96ba11441bc8957bb68a71459dd37275a78ea90238ad5f01", size = 264344, upload-time = "2026-03-04T04:15:00.721Z" }, + { url = "https://files.pythonhosted.org/packages/84/fd/9d8a0594aedcc6ee8133b21dd3543d6021842e013058803a69918720d0aa/datamodel_code_generator-0.72.3-py3-none-any.whl", hash = "sha256:4536f6dd12dd86c9c7a78b16ef83ae9aa7a9cbdb8bef8a3b63baa4ad9d3d3829", size = 554226, upload-time = "2026-08-10T18:58:40.087Z" }, ] [[package]] @@ -429,6 +431,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jsondiff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/48/841137f1843fa215ea284834d1514b8e9e20962bda63a636c7417e02f8fb/jsondiff-2.2.1.tar.gz", hash = "sha256:658d162c8a86ba86de26303cd86a7b37e1b2c1ec98b569a60e2ca6180545f7fe", size = 26649, upload-time = "2024-08-29T04:09:06.201Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/94/a8066f84d62ab666d61ef97deba1a33126e3e5c0c0da2c458ada17053ed6/jsondiff-2.2.1-py3-none-any.whl", hash = "sha256:b1f0f7e2421881848b1d556d541ac01a91680cfcc14f51a9b62cdf4da0e56722", size = 13440, upload-time = "2024-08-29T04:09:04.955Z" }, +] + [[package]] name = "jsonschema" version = "4.25.1"