Skip to content

Commit c88ea9f

Browse files
update skill, add instruction on getting api keys, etc.
1 parent 20a1557 commit c88ea9f

14 files changed

Lines changed: 349 additions & 103 deletions

README.md

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,47 @@ Quickly useful notes:
1616

1717
### 1. Obtain an API Key
1818

19-
Get your API key from [COMcheck Web](https://comcheck.energycode.pnl.gov):
19+
Get a Personal Access Token from the new
20+
[COMcheck Web site](https://comcheck.energycode.pnl.gov):
2021

21-
1. Log in to your COMcheck Web account
22-
2. Navigate to **Settings → Developer Setting**
23-
3. Generate and copy your API key
22+
1. Log in (or register a new account if you don't have one).
23+
2. Click your **username** in the left-side navigation.
24+
3. From the menu that appears, choose **Settings**.
25+
4. Click **Developer Setting** to open the Personal Access Token
26+
page.
27+
5. Click **Generate**, then immediately copy the token.
2428

25-
> **Note:** The Developer Setting feature is currently under development.
29+
> **Important:** the token is shown **only once**. Save it somewhere
30+
> safe (a password manager, your `.env`, etc.) before leaving the
31+
> page. If you lose it, generate a new one — the old one will stop
32+
> working.
2633
2734
### 2. Configure the API Key
2835

29-
For detailed setup instructions, see the [Getting Started](https://pnnl-int.github.io/comcheckweb-api-python/getting-started/) guide.
36+
Create a `.env` file at the root of your project and add:
37+
38+
```
39+
COM_API_KEY=<your-api-key-here>
40+
```
41+
42+
The SDK does **not** auto-load this — you read it yourself and pass
43+
it to the client. With `python-dotenv` this is two lines:
44+
45+
```python
46+
import os
47+
from dotenv import load_dotenv
48+
from comcheck_api import COMcheckClient
49+
50+
load_dotenv()
51+
client = COMcheckClient(api_key=os.environ["COM_API_KEY"])
52+
```
53+
54+
(`client.set_api_key(api_key)` is the equivalent post-construction
55+
setter if you'd rather defer.)
56+
57+
For more detail, see the
58+
[Getting Started](https://pnnl.github.io/comcheckweb-api-python/getting-started/)
59+
guide.
3060

3161
### 3. Install the Package
3262

@@ -39,7 +69,7 @@ pip install comcheckweb-api-python
3969
- **Simulation only:** You can use the simulation features directly without creating a project on COMcheck Web.
4070
- **Updating a project:** You must first create the project under your account on [COMcheck Web](https://comcheck.energycode.pnl.gov) before using this package to update it. Project creation is not yet supported through this package.
4171

42-
For detailed usage examples and API reference, see the [documentation](https://pnnl-int.github.io/comcheckweb-api-python/).
72+
For detailed usage examples and API reference, see the [documentation](https://pnnl.github.io/comcheckweb-api-python/).
4373

4474
## Introspection helpers
4575

@@ -67,7 +97,7 @@ if not result.ok:
6797
print(err.loc, err.msg)
6898
```
6999

70-
See [`api/introspection`](https://pnnl-int.github.io/comcheckweb-api-python/api/introspection/)
100+
See [`api/introspection`](https://pnnl.github.io/comcheckweb-api-python/api/introspection/)
71101
in the docs for the full reference.
72102

73103
## AI integration: the Claude Skill
@@ -105,7 +135,7 @@ pass `--global` (writes to `~/.claude/skills/comcheck-api/`).
105135
Clone the repository and follow the commands below to set up developer tooling.
106136

107137
```bash
108-
git clone https://github.com/pnnl-int/comcheckweb-api-python.git
138+
git clone https://github.com/pnnl/comcheckweb-api-python.git
109139
cd comcheckweb-api-python
110140
uv sync
111141
```

comcheck_api/ai/skill/SKILL.md

Lines changed: 127 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,47 +22,62 @@ Triggers:
2222
## Core concepts
2323

2424
- **Single entry point**: `COMcheckClient` is the only client class
25-
users instantiate. Construct with `api_key=...` or rely on the
26-
`COM_API_KEY` env var.
27-
- **Project shape**: a project is a `ComBuilding` Pydantic model. It
28-
contains: `Project` (metadata), `Location`, `Envelope`, `WholeBldgUse[]`
29-
(building areas), `HVAC`, `Lighting`, `Renewable`, and `Control`.
30-
- **Operation classes (functional)**: building areas and envelope
25+
users instantiate. Construct with `api_key=...`. The client does
26+
**not** auto-read any environment variable — read it yourself and
27+
pass it: `COMcheckClient(api_key=os.environ["COM_API_KEY"])`.
28+
- **Project shape**: a project is a `ComBuilding` Pydantic model with
29+
lowercase top-level fields: `project` (metadata), `location`,
30+
`envelope`, `lighting` (which contains `wholeBldgUse[]` — the
31+
building areas), `hvac`, `renewable`, and `control` (energy code).
32+
No `Project`/`Control` PascalCase aliases exist.
33+
- **Operation modules (functional)**: building areas and envelope
3134
components are added/updated/removed via free functions in
3235
`project_building_area_operations` and `project_envelope_operations`.
3336
Each function takes a `ComBuilding` and returns a new `ComBuilding`.
37+
- **Envelope items attach to a building-area key**: every
38+
`add_*_to_project` envelope function takes
39+
`(project, building_area_key, new_component)`. Look up the key
40+
with `ba_ops.get_building_area_keys_from_project(project)` first.
41+
Default projects have **no** building areas — add one with
42+
`ba_ops.add_building_area_to_project(...)` before any envelope
43+
work.
3444
- **Defaults**: `comcheck_api.defaults` has `get_default_*_template()`
3545
functions that return Pydantic models filled with sensible defaults.
3646
Always start from these.
3747
- **Simulation flow is async**: `start_run_simulation` returns a
38-
session ID. Poll `get_simulation_status` until status is `complete`,
39-
then call `get_simulation_result`.
48+
session ID. Poll `get_simulation_status` until status is
49+
`"SUCCESS"` (terminal-ok) or `"FAILED"` (terminal-error), then call
50+
`get_simulation_result`. See `comcheck_api.types.SimulationStatus`
51+
for known lifecycle values (the catalog isn't exhaustive — only
52+
the terminal pair is guaranteed stable).
4053

4154
## Quick start
4255

4356
```python
57+
import os
58+
import time
59+
4460
from comcheck_api import COMcheckClient
4561
from comcheck_api.defaults import get_default_project_template
62+
from comcheck_api.types import SimulationStatus
4663

47-
client = COMcheckClient(api_key="your-key")
64+
# Client does NOT auto-read COM_API_KEY — pass it in.
65+
client = COMcheckClient(api_key=os.environ["COM_API_KEY"])
4866

4967
# Build a project from a default template
5068
project = get_default_project_template()
51-
project.Project.title = "5,000 sqft office in Seattle"
52-
53-
# Save it (creates server-side project, returns ID via list)
54-
# Update if you have an existing ID:
55-
# updated = client.update_project(project_id="123", project_data=project)
69+
project.project.projectTitle = "5,000 sqft office in Seattle"
5670

5771
# Run a simulation
5872
session_id = client.start_run_simulation(project)
5973

60-
# Poll until complete
61-
import time
74+
# Poll until terminal — SUCCESS or FAILED
6275
while True:
6376
status = client.get_simulation_status(session_id)
64-
if status["status"] == "complete":
77+
if status["status"] == SimulationStatus.SUCCESS:
6578
break
79+
if status["status"] == SimulationStatus.FAILED:
80+
raise RuntimeError(f"Simulation failed: {status.get('message')}")
6681
time.sleep(5)
6782

6883
result = client.get_simulation_result(session_id)
@@ -73,11 +88,33 @@ print(result["performanceRating"])
7388

7489
- Use `get_default_*_template()` to start any new component, then
7590
customize. Don't construct `ComBuilding` from scratch.
76-
- Use the operation classes (e.g.,
77-
`project_envelope_operations.add_ag_wall_to_project(project, wall)`)
78-
to mutate project structure. Don't manipulate nested dicts directly.
79-
- Read the API key from `COM_API_KEY` env var by default; let users
80-
pass `api_key=...` to override.
91+
- Use the operation modules (e.g.,
92+
`env_ops.add_ag_wall_to_project(project, area_key, wall)`) to
93+
mutate project structure. Don't manipulate nested dicts directly.
94+
- For envelope items, look up the building-area key first via
95+
`ba_ops.get_building_area_keys_from_project(project)`. The key
96+
goes between `project` and the new component in every
97+
`add_*_to_project` envelope call.
98+
- Set enum-typed fields (`orientation`, `wallType`, `code`, etc.)
99+
with members from the matching `*Options` enum imported from
100+
`comcheck_api.types` — not raw strings, which trigger
101+
Pydantic serialization warnings.
102+
- Pass `api_key=` explicitly to `COMcheckClient(...)`. The SDK does
103+
**not** auto-load any env var — read it yourself:
104+
105+
```python
106+
from dotenv import load_dotenv
107+
load_dotenv()
108+
client = COMcheckClient(api_key=os.environ["COM_API_KEY"])
109+
```
110+
111+
`client.set_api_key(api_key)` is the equivalent post-construction
112+
setter. Users get a Personal Access Token from the COMcheck Web
113+
site (Settings → Developer Setting); see the
114+
[GitHub README](https://github.com/pnnl/comcheckweb-api-python#1-obtain-an-api-key)
115+
or the
116+
[Getting Started page](https://pnnl.github.io/comcheckweb-api-python/getting-started/)
117+
for the full walkthrough.
81118
- Wrap network calls in try/except and catch `COMCheckHTTPError`,
82119
`COMCheckConnectionError`, `COMCheckValidationError`,
83120
`COMCheckSimulationError`, `COMCheckProjectNotFoundError`.
@@ -91,47 +128,104 @@ print(result["performanceRating"])
91128
- Don't import private modules (anything starting with `_`).
92129
- Don't poll `get_simulation_status` faster than every 5 seconds.
93130
- Don't put the API key in source code; use env var or argument.
131+
- Don't use `comcheck_api.managers.*` (e.g. `AgWallListManager`,
132+
`RoofListManager`). Those are internal list-mutation helpers; they
133+
bypass the validation logic in the operation modules. Always go
134+
through `project_envelope_operations` and
135+
`project_building_area_operations` instead.
94136

95137
## Common patterns
96138

97-
### Adding an above-grade wall
139+
### Adding a building area, then an above-grade wall
98140

99-
```python
100-
from comcheck_api import project_envelope_operations as envelope_ops
101-
from comcheck_api.defaults import get_default_ag_wall_template
141+
`get_default_project_template()` starts with **zero building
142+
areas** — add one before attaching any envelope component.
102143

144+
```python
145+
from comcheck_api import (
146+
project_envelope_operations as env_ops,
147+
project_building_area_operations as ba_ops,
148+
)
149+
from comcheck_api.defaults import (
150+
get_default_building_area_template,
151+
get_default_ag_wall_template,
152+
)
153+
from comcheck_api.types import OrientationOptions
154+
155+
# 1. Add the building area (no areas exist by default).
156+
area = get_default_building_area_template()
157+
area.areaDescription = "Open office"
158+
project = ba_ops.add_building_area_to_project(project, area)
159+
160+
# 2. Look up its key.
161+
area_key = ba_ops.get_building_area_keys_from_project(project)[0]["key"]
162+
163+
# 3. Attach the wall to that area.
103164
wall = get_default_ag_wall_template()
104-
wall.name = "South wall"
105-
wall.area = 4800.0
106-
project = envelope_ops.add_ag_wall_to_project(project, wall)
165+
wall.description = "South wall"
166+
wall.orientation = OrientationOptions.SOUTH # use the enum, not "SOUTH"
167+
wall.grossArea = 4800.0 # field is grossArea, not area
168+
project = env_ops.add_ag_wall_to_project(project, area_key, wall)
107169
```
108170

109171
### Listing the user's projects and updating one
110172

111173
```python
112174
projects = client.list_projects()
113175
target = next(p for p in projects if p["title"] == "My office")
114-
project_obj = client.get_project(target["id"])
115-
project_obj.Project.title = "My office (revised)"
116-
client.update_project(project_id=target["id"], project_data=project_obj)
176+
project_obj = client.get_project(target["_id"]) # note the underscore
177+
project_obj.project.projectTitle = "My office (revised)"
178+
client.update_project(project_id=target["_id"], project_data=project_obj)
117179
```
118180

181+
`get_project(project_id, mode="json")` returns a raw dict instead of
182+
a `ComBuilding` model — handy when you just need the JSON shape.
183+
119184
### Polling a simulation with a timeout
120185

121186
```python
122187
import time
188+
from comcheck_api.types import SimulationStatus
189+
123190
session_id = client.start_run_simulation(project)
124191
deadline = time.time() + 300 # 5 min
125192
while time.time() < deadline:
126193
status = client.get_simulation_status(session_id)
127-
if status["status"] == "complete":
194+
if status["status"] == SimulationStatus.SUCCESS:
128195
result = client.get_simulation_result(session_id)
129196
break
197+
if status["status"] == SimulationStatus.FAILED:
198+
raise RuntimeError(f"Simulation failed: {status.get('message')}")
130199
time.sleep(5)
131200
else:
132201
raise TimeoutError(f"Simulation {session_id} did not complete in 5 min")
133202
```
134203

204+
## Gotchas
205+
206+
- **Field names are lowercase camelCase**, not PascalCase.
207+
`project.project.projectTitle`, `project.control.code`,
208+
`project.lighting.wholeBldgUse[0].areaDescription`. There is no
209+
`Project`, `Control`, `WholeBldgUse` (top-level), or `.title`.
210+
- **AG/BG wall area field is `grossArea`**, not `area`. Same for
211+
roofs/floors/windows/doors. Setting `.area` silently does nothing
212+
because Pydantic models reject unknown attributes only in strict
213+
mode.
214+
- **Use enum members for typed fields**: `OrientationOptions.NORTH`,
215+
`WallTypeOptions.WOOD_FRAME_16_AG_WALL`,
216+
`EnergyCodeOptions.CEZ_90_1_2022`. Setting them to raw strings
217+
works at runtime but emits `PydanticSerializationUnexpectedValue`
218+
warnings every time the model serializes.
219+
- **`COMcheckClient(api_key=...)` does not auto-read env vars.** No
220+
`COM_API_KEY` fallback exists in `__init__`. Pass it explicitly.
221+
- **`SimulationStatus` is a known-values catalog, not an exhaustive
222+
contract.** The server may emit lifecycle values not yet in the
223+
enum (e.g. `EVALUATING` was added in a later version). The
224+
`status` field comes back as a plain `str` so unknown values
225+
don't crash polling. Only `SUCCESS` and `FAILED` are guaranteed
226+
terminal — break/raise on those, keep polling for everything
227+
else (don't enumerate non-terminals).
228+
135229
## When you need more detail
136230

137231
- For envelope assemblies (roof, walls, floor, windows, doors,

0 commit comments

Comments
 (0)