Skip to content

๐ŸŽจ Airport Webcams Special Liveries โ€‹

Nameairportwebcams-special-liveries
ScopeGlobal โ€” not country-specific, like mictronics / ourairports
Data sourcehttps://airportwebcams.net/special-liveries/
FormatStatic server-rendered HTML table (WordPress TablePress, id tablepress-8)
Run frequencyWeekly (Friday, 06:10 UTC)
Depends on Mictronics for ICAO hexYes โ€” this source does not publish ICAO hex addresses; registrations are resolved via RediSearch against Mictronics records (idx:aircraft:mictronics). Must run after the mictronics runner.

How it works โ€‹

The special-liveries page is fetched with a single GET (a browser-like User-Agent is required โ€” the site's ModSecurity rule blocks the default curl/requests User-Agent with HTTP 406, though an honest identifying UA such as "Mozilla/5.0 (compatible; P5Software SkyFollower)" is accepted). The entire ~2,074-row table is server-rendered into the initial HTML response, so no pagination or AJAX crawling is needed; the DataTables JS on the live page only adds client-side search/sort on top of it.

The Registration cell (tail number) is resolved to icao_hex in batches via a RediSearch query against the Mictronics index, the same pattern used by at-austrocontrol-registry, bz-bdca-registry, and the other hex-less country runners. Rows whose Registration is the literal string Various (whole-fleet special liveries โ€” e.g. a "10 Years" sticker applied across an entire fleet) are skipped, since they can't be resolved to a single aircraft. Registrations with no Mictronics match are also skipped and logged as a miss, not treated as an error.

The Description cell is transformed into special_livery rather than stored verbatim, because this field is spoken aloud by Home Assistant TTS on a matched-flight notification and needs to read as a clean phrase. special_livery holds the derived livery name directly โ€” there's no separate boolean flag; presence of the field on the merged aircraft record is itself the signal that the aircraft is wearing a special livery:

  1. Strip any parenthetical annotation whose contents contain sticker or #new (case-insensitive) โ€” this covers (sticker), (stickers), (sticker, underside), (sticker; underside/belly), and the site's (#New at DD-Mon-YY) freshness marker in whatever exact wording/date format it uses. This step runs before the split below, not after โ€” at least one real annotation contains its own / ("(sticker; underside/belly)"), which would otherwise be misread as an extra compound-description segment.
  2. Split what remains on / and take the last segment โ€” roughly 7% of rows pack more than one livery name into a single cell (e.g. "50th Anniversary / Air Silk Road / 1 Million Tons, 10 Years"); the last-listed one is treated as the current/primary livery.
  3. Collapse whitespace and trim.

Parenthetical content that's genuinely part of the livery name (e.g. a year like (2022)) is left alone โ€” only annotations containing sticker/#new are stripped. Verified against all 2,074 real rows in the live table: the transform never produces an empty value.

Every matched row writes aircraft:livery:{icao_hex} = {"special_livery": "<transformed>", "source": "airportwebcams-special-liveries"} with the same 14-day TTL used by the other registration-enrichment keys. shared/lua/merge_aircraft.lua reads this key in addition to aircraft:mictronics:{icao_hex} and aircraft:registry:{icao_hex}, deep-merging it last so it takes priority over both โ€” Mictronics on the bottom, country registry in the middle, special livery on top.

Columns โ€‹

Source columnImportedNotes
CountryโŒPresent in source; not read by this runner
AirlineโŒPresent in source; not read by this runner
Aircraft TypeโŒPresent in source; not read by this runner โ€” type/manufacturer data already comes from Mictronics/registry runners
Registrationโœ…Used only as the Mictronics RediSearch lookup key to resolve icao_hex; not stored on the record itself. Rows with value Various (whole-fleet special liveries) are skipped
Descriptionโœ…โ†’ special_livery, transformed: strip a sticker/#new parenthetical annotation, split on /, take the last segment

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

Example transform โ€‹

Description (source)special_livery (stored)
Rugby's Greatest Rivalry / 2026 NZ v SA rugby tour (#New at 17-Jul-26)2026 NZ v SA rugby tour
50th Anniversary / Air Silk Road / 1 Million Tons, 10 Years (stickers)1 Million Tons, 10 Years
Amar Bank (sticker) (#New at 17-Jul-26)Amar Bank
Vets In Blue (2022) / America250 (sticker)America250
FedEx founder F W Smith (sticker; underside/belly)FedEx founder F W Smith
SkyTeamSkyTeam

Example Output โ€‹

Read back the merged record for a given ICAO hex (combines this runner's data with Mictronics, any country registry runner, and this runner, whichever have written to the same key). AA7C64 is JetBlue N775JB โ€” verified live against the FAA registry N-number lookup โ€” carrying the "Vets In Blue (2022) / America250 (sticker)" livery from the source table:

bash
docker run --rm --network host redis:latest redis-cli EVAL "$(cat ./shared/lua/merge_aircraft.lua)" 0 AA7C64 | python3 -m json.tool --sort-keys --no-ensure-ascii
json
{
    "aircraft": {
        "manufacturer_model": "..."
    },
    "icao_hex": "AA7C64",
    "registration": "N775JB",
    "special_livery": "America250"
}

(aircraft.manufacturer_model above is illustrative only โ€” whatever Mictronics/registry already has on file for this hex; icao_hex, registration, and special_livery are what this runner actually verifies.)

For comparison, an aircraft with no special livery โ€” e.g. the Delta 757 (N659DL / A8AE7F) used as an example in ../us-faa-registry/README.md โ€” merges exactly as it does today, with no special_livery key present at all.

Configuration โ€‹

See Data Runners for the full list of environment variables every runner reads. This runner writes aircraft:livery:{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/airportwebcams-special-liveries/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_airportwebcams_special_liveries_{name}/config for each of the three stats above.