Skip to content

Singapore ๐Ÿ‡ธ๐Ÿ‡ฌ CAAS Registry โ€‹

Namesg-caas-registry
CountrySingapore
Registration prefix9V-
Data sourcehttps://www.caas.gov.sg/industry/aircraft-operators/certificate-of-registration/
FormatXLSX (UUID-bearing filename, discovered via index page)
Run frequencyMonthly (day 1, 10:10 UTC)
Depends on Mictronics for ICAO hexYes โ€” the CAAS register does not publish ICAO hex (Mode S) addresses; registrations are resolved via RediSearch against Mictronics records (idx:aircraft:mictronics). Must run after the mictronics runner.

How it works โ€‹

The register xlsx's filename includes a UUID component that changes every month, so the download URL is discovered fresh each run by scraping the CAAS certificate-of-registration page for a link on the isomer-user-content.by.gov.sg CDN ending in .xlsx. The CDN rejects requests without a browser User-Agent and a Referer header pointing back to the index page. The workbook is opened read-only with openpyxl; the first non-blank row is treated as the header row, and subsequent rows are kept only if their first cell starts with 9V-. Every written record explicitly sets military: false โ€” this register is exclusively civil, and the explicit value ensures a stale military: true flag (from Mictronics or a prior record on a reused hex) is corrected on re-registration.

Columns โ€‹

Source columnImportedNotes
Aircraft Registrationโœ…9V-prefix; used as the Mictronics lookup key
Aircraft Manufacturerโœ…โ†’ aircraft.manufacturer
Aircraft Modelโœ…โ†’ aircraft.model
Aircraft S/Nโœ…โ†’ aircraft.serial_number
Engine Manufacturerโœ…โ†’ aircraft.powerplant.manufacturer
Engine Modelโœ…โ†’ aircraft.powerplant.model
Operatorโœ…โ†’ registrant.names[0]

See specs/data-dictionary.yaml (sg-caas-registry entry) for full column semantics and cross-source schema notes.

Example Output โ€‹

Read back the merged record for a given ICAO hex (combines this runner's data with Mictronics and any other sources that have written to the same key):

Note: the Singapore register is almost entirely widebody airliners; the small example below (a Diamond DA40) is one of the few GA singles on the register.

bash
docker run --rm --network host redis:latest redis-cli EVAL "$(cat ./shared/lua/merge_aircraft.lua)" 0 76E4C7 | python3 -m json.tool --sort-keys --no-ensure-ascii
json
{
    "aircraft": {
        "manufacturer": "Diamond",
        "manufacturer_model": "DIAMOND DA-40 Club Star",
        "model": "DA40",
        "powerplant": {
            "manufacturer": "Lycoming",
            "model": "Lycoming IO-360-M1A"
        },
        "serial_number": "40.1089",
        "type_designator": "DA40"
    },
    "data_sources": [
        "mictronics",
        "sg-caas-registry"
    ],
    "icao_hex": "76E4C7",
    "military": false,
    "registrant": {
        "names": [
            "Singapore Youth Flying Club"
        ]
    },
    "registration": "9V-YFG"
}
bash
docker run --rm --network host redis:latest redis-cli EVAL "$(cat ./shared/lua/merge_aircraft.lua)" 0 76CD76 | python3 -m json.tool --sort-keys --no-ensure-ascii
json
{
    "aircraft": {
        "manufacturer": "Airbus",
        "manufacturer_model": "AIRBUS A-380-800",
        "model": "A380-841",
        "powerplant": {
            "manufacturer": "Rolls-Royce",
            "model": "RB211-TRENT 970"
        },
        "serial_number": "247",
        "type_designator": "A388"
    },
    "data_sources": [
        "mictronics",
        "sg-caas-registry"
    ],
    "icao_hex": "76CD76",
    "military": false,
    "registrant": {
        "names": [
            "Singapore Airlines Limited"
        ]
    },
    "registration": "9V-SKV"
}

Configuration โ€‹

See Data Runners for the full list of environment variables every runner reads. This runner writes aircraft:registry:{icao_hex} with a fixed 14-day TTL (ENRICHMENT_TTL_SECONDS in shared/timing.py).

MQTT โ€‹

Published once, at the end of a run, to SkyFollower/runner/sg-caas-registry/statistic/{name} (all retained):

Topic suffixValueFormat
records_importede.g. 271Integer as string
last_run_ate.g. 2026-07-07T14:32:01.123456+00:00ISO 8601 UTC
last_run_statusSuccess or FailureString

Home Assistant autodiscovery configs are also published (retained) to homeassistant/sensor/SkyFollower_runner_sg_caas_registry_{name}/config for each of the three stats above.