BRAVO Search Skill
Search for and download building records from the Hong Kong Buildings Department BRAVO (Building Records Access and Viewing On-line) system. Works for any project — not tied to a specific building or lot.
Prerequisites
- Use
playwright-clifor browser automation. agent-browseris NOT suitable for BRAVO (element interaction issues).- Ask the user for BRAVO credentials if not already provided.
Login Flow
BRAVO has a multi-step login. These steps are identical for all users:
- Navigate to
https://bravo.bd.gov.hk/login?request_locale=zh_HK - Fill
textbox "登入名稱"with username - Fill
textbox "密碼"with password - Click
button "登入" - On the notice page (
/notice):- Check
checkbox "本人確認已閱讀..." - Click
button "接受"
- Check
- On the declaration page (
/noticeProcess.action):- Check
checkbox "選項 A"(building construction matters) - Click
button "遞交"
- Check
- On the multi-login page (
/checkMulitpleSignOn):- Click
button "是"
- Click
- Now at the home page (
/home)
Always take a fresh snapshot after each page transition to get current element refs. Refs change between sessions.
Location Search with Autocomplete
BRAVO uses jQuery UI autocomplete for address search.
Correct method:
- Ask the user for the address or lot to search
- Find
textbox "位置"on the home page, click to focus - Use
playwright-cli type "<address>"— type character-by-character - Wait ~2-3 seconds for autocomplete dropdown
- Click the
.ui-menu-item-wrapperelement in the dropdown - Click
button "搜尋"or press Enter - If results panel is collapsed, click
button "只顯示搜尋結果"
Wrong methods (will NOT work):
fillcommand — no autocomplete trigger- Direct API calls — require Daeja-specific auth, not session cookies
gototo internal pages — kills the session
Navigating Search Results
Results appear in a table with columns:
- 圖則/文件/小型工程 | 檔案編號 | 地址 | 大廈名稱 | 地段編號 | 檔案類型 | OP編號 | 備註
To open a record's plan list:
- Find the row matching the target file number (e.g.,
3/4003/72) - Click the
button "圖則"in that row - A new tab opens with the plan list at
viewPlanList?recId=<recId> - Switch to the new tab with
tab-select
Each plan in the list has:
button "Preview"— thumbnail view (low res, full plan)button "Inspect Full Image"— Daeja ViewONE viewer
Downloading High-Resolution Plans via Tile API
The Daeja ViewONE viewer often shows "Lost connection to the server" while the actual tile API remains functional. This is the most reliable method for full-resolution plan extraction.
Step 1: Navigate and Get Base Resolution + Auth Tokens
Navigate to the inspection page for a plan and extract both the base dimensions and auth tokens from the tile request:
var entries = performance.getEntriesByType('resource');
for (var e of entries) {
if (e.name.includes('op=tile') && e.name.includes('tw=')) {
var url = e.name;
var getP = function(n) {
var m = url.match(new RegExp('[&?]' + n + '=([^&]*)'));
return m ? m[1] : '';
};
var baseW = parseInt(getP('tw')); // e.g., 794
var baseH = parseInt(getP('th')); // e.g., 554
var tokens = {
fch: getP('fch'), xCCH: getP('X-CCH'), xCDH: getP('X-CDH'),
xCId: getP('X-CId'), xLCId: getP('X-LCId'),
version: getP('v') || '45571'
};
// Use these for the HD request
}
}
The tw and th values tell you the base resolution at 96 DPI. Multiply
by the desired scale factor to get target HD dimensions.
Step 2: Request HD Tile
Construct a tile URL with increased DPI. The scale factor determines output quality — 6x gives ~4764px for a typical 794px base, suitable for 300 DPI printing at A3 size.
https://bravo.bd.gov.hk/v1files/?v=<version>&op=tile
&tw=<baseW * scale>&th=<baseH * scale>&x=0&y=0&page=1
&DPI=<96 * scale>&rxr=<96 * scale>&ryr=<96 * scale>
&w=<baseW * scale>&h=<baseH * scale>&enh=1&iv=0
&fch=<fch>&err=0
&X-CCH=<xCCH>&X-CDH=<xCDH>&X-CId=<xCId>&X-LCId=<xLCId>
&X-Client-Version=<version>
This returns the FULL plan as a single PNG tile. Unlike zoom+canvas extraction, this captures the entire drawing at once.
Step 3: Base64 Chunking and Download
Large HD images require chunked base64 encoding inside page.evaluate()
to avoid "Maximum call stack size exceeded":
const arr = new Uint8Array(await blob.arrayBuffer());
let b64 = '';
for (let i = 0; i < arr.length; i += 8192) {
b64 += String.fromCharCode.apply(null, arr.subarray(i, i + 8192));
}
return { b64: btoa(b64), size: arr.length };
Then decode in Python and save via Pillow:
data = base64.b64decode(result["b64"])
with open(output_path, "wb") as f:
f.write(data)
Step 4: Combine to PDF
Use Pillow to combine all downloaded PNGs:
from PIL import Image
images = [Image.open(f).convert("RGB") for f in png_files]
images[0].save("output.pdf", "PDF", save_all=True,
append_images=images[1:], resolution=300)
Key Constraints
- Auth tokens are unique per inspection page load — must extract fresh for each plan
- Different plans may have different base resolutions (
tw×th) - Canvas extraction at any zoom level captures only visible tiles
- Fit-to-window gives full plan but at base 96 DPI resolution
- Direct
getInspectionPlanImageURL requires Daeja auth headers - Scale factor choice: 6x is a safe default; higher may hit server limits
Common Issues
- Modal backdrop: Close with
evalon#systemMsgModal button - Autocomplete 503: Transient server issue; retry in a few minutes
- Session lost on navigation: Use
history.back()or tab switching, nevergototo internal URLs - "Lost connection to the server": Daeja viewer UI issue only; tile API still works — proceed with HD tile download method
微信扫一扫