๐ฒ๐ป mv-caa โ
| Country | Maldives |
| Registration prefix | 8Q- |
| Data source | https://www.caa.gov.mv/operations/registration-of-aircraft-and-mortgages |
| Format | PDF (opaque hash filename, discovered via index page) |
| Run frequency | Weekly (Tuesday, 15:10 UTC) |
| Depends on Mictronics for ICAO hex | Yes โ this runner does not publish ICAO hex addresses; 8Q- registrations are resolved via RediSearch against Mictronics records (idx:aircraft:mictronics). Must run after the mictronics runner. |
How it works โ
The CAA index page is scraped for the first link ending in .pdf (its filename is an opaque hash rather than a predictable pattern, so any PDF href found is taken). Each page of the PDF is parsed with pdfplumber's extract_table(); only rows whose registration column starts with 8Q- and whose status column reads exactly "Valid" are kept. The combined Manufacturer+Designation column has embedded newlines collapsed to single spaces. The Owner Name+Address column is multi-line: the first line is taken as the owner name, the last line as the country, and any lines in between as the street address (joined as a list). Serial number is also whitespace-collapsed like the Manufacturer+Designation column, since pdfplumber can represent a wrapped cell's text with an embedded newline rather than a space. 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 column | Imported | Notes |
|---|---|---|
| N/S (sequence) | โ | Parsed but not stored |
| Certificate# | โ | Parsed but not stored |
| Registration | โ | 8Q-prefix; used as the Mictronics lookup key |
| Manufacturer+Designation | โ | โ aircraft.model; embedded newlines collapsed to spaces |
| MTOW | โ | Parsed but not stored |
| Serial Number | โ | โ aircraft.serial_number; embedded newlines collapsed to a single space |
| Year Built | โ | โ aircraft.manufactured_date (YYYY-01-01), only if a 4-digit year |
| Owner Name+Address | โ | Multi-line: first line โ registrant.names[0], last line โ registrant.country, middle lines โ registrant.street (list) |
| Legal Owner | โ | Parsed but not stored |
| Registration Basis | โ | Parsed but not stored |
| Basis of Registration, Other Specifics | โ | Present in source; not read by this runner |
| Mortgage | โ | Parsed but not stored |
| IDERA | โ | Present in source; not read by this runner |
| Original Issue Date | โ | Parsed but not stored |
| Last Revision Date | โ | Parsed but not stored |
| Status | โ | Used as a filter โ only rows with status "Valid" are kept; the status value itself is not stored |
See specs/data-dictionary.yaml (mv-caa 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):
docker run --rm --network host redis:latest redis-cli EVAL "$(cat ./shared/lua/merge_aircraft.lua)" 0 05A097 | python3 -m json.tool --sort-keys --no-ensure-ascii{
"aircraft": {
"manufactured_date": "1977-01-01",
"manufacturer": "CESSNA",
"manufacturer_model": "CESSNA 152",
"model": "Textron Aviation Inc., 152",
"serial_number": "152-80719",
"type_designator": "C152",
"wake_turbulence_category": "Light"
},
"icao_hex": "05A097",
"military": false,
"registrant": {
"country": "Republic of Maldives",
"names": [
"Zenith Aviation Academy Pvt. Ltd."
],
"street": [
"Gan International Airport",
"S. Gan, 19020"
]
},
"registration": "8Q-MFB",
"source": "mv-caa"
}docker run --rm --network host redis:latest redis-cli EVAL "$(cat ./shared/lua/merge_aircraft.lua)" 0 05A0AF | python3 -m json.tool --sort-keys --no-ensure-ascii{
"aircraft": {
"manufactured_date": "2010-01-01",
"manufacturer": "AIRBUS",
"manufacturer_model": "AIRBUS A-330-200",
"model": "Airbus, Airbus A330-203",
"serial_number": "1161",
"type_designator": "A332",
"wake_turbulence_category": "Heavy"
},
"icao_hex": "05A0AF",
"military": false,
"registrant": {
"country": "Republic of Maldives",
"names": [
"Island Aviation Services Limited"
],
"street": [
"M. Dar Al-Eiman Building, Majeedhee Magu",
"Maleโ 20345"
]
},
"registration": "8Q-IAB",
"source": "mv-caa"
}Configuration โ
Reads settings.json (mounted at /app/settings.json):
| Parameter | Required | Default | Notes |
|---|---|---|---|
redis.host | โ | โ | Redis connection host |
redis.port | โ | 6379 | |
mqtt.host | โ | โ | Omit the whole mqtt block to skip completion-stats publishing entirely |
mqtt.port | โ | 1883 | |
mqtt.username | โ | โ | Optional MQTT auth; omit for an anonymous broker |
mqtt.password | โ | โ | |
redis_ttl_days | โ | 14 | TTL applied to each aircraft:registry:{icao_hex} key written by this runner |
MQTT โ
Published once, at the end of a run, to SkyFollower/runner/mv-caa/statistic/{name} (all retained):
| Topic suffix | Value | Format |
|---|---|---|
records_imported | e.g. 271 | Integer as string |
last_run_at | e.g. 2026-07-07T14:32:01.123456+00:00 | ISO 8601 UTC |
last_run_status | success or failure | String |
Home Assistant autodiscovery configs are also published (retained) to homeassistant/sensor/SkyFollower_runner_mv_caa_{name}/config for each of the three stats above.