Skip to main content
Data Connectors · Weather

OpenUV Index

The node queries the OpenUV.io API for the current UV index, ozone value and safe exposure times for one or more locations. Optionally it also delivers the hourly UV forecast of the day. Results are emitted as NGSI-LD style objects of type UVIndexObservation and UVIndexForecast.

  • Data source
  • Trigger-based
  • API key
Node type
openuv
Package
@htag/node-red-contrib-hypertegrity-weather
Inputs / outputs
1 / 2
Data source
OpenUV.io API (api.openuv.io/api/v1)

When to use this node​

The UV index describes the sunburn-effective radiation and is an important indicator for health protection in public spaces. OpenUV.io provides this value in real time for any coordinates, calculated from satellite data on the ozone layer. The OpenUV Index node integrates the service into Node-RED and handles authentication, the query per location and the transformation into a uniform data model.

Typical use cases are:

  • UV advice for outdoor pools, schoolyards and playgrounds on display boards or in a citizen app.
  • Municipal health dashboards with UV index, ozone and safe exposure times per skin type.
  • Daily UV profile as a forecast for event planning or irrigation control.

How the node works​

1

Trigger

An incoming message starts the fetch. The node never polls OpenUV.io on its own.

2

Query

For every location the /uv endpoint is called with latitude and longitude; with forecast enabled, /forecast is called as well.

3

Transform

The response is mapped to a UVIndexObservation object; every forecast point becomes its own UVIndexForecast object.

4

Emit

Output 1 receives one observation per location, output 2 one message per forecast hour.

Result: UV observations and forecasts with geoposition that can be passed directly to the NGSI-LD Builder.

The node is trigger-based. Combine it with an Inject node that fires at a fixed interval. Keep the quota of your OpenUV.io plan in mind: every trigger causes one request per location and, with forecast enabled, one more. Locations are queried one after another; the timeout per request is 10 seconds.

Configuration​

The edit dialog consists of the Name field and two tabs.

Tab "Locations"​

FieldTypeDefaultMeaning
NameTextemptyDisplay name of the node in the flow.
Map selectionMap–A click on the map places a point; "Add Selected Point" adds it to the list. Coordinates already present are rejected with "This location has already been added".
Selected locations (locations)ListemptyEntries with location name (name), latitude (lat) and longitude (lon). The name becomes part of the entity ID and appears in locationName.

Tab "Settings"​

FieldTypeDefaultMeaning
API Key (apiKey)Password field, requiredemptyYour OpenUV.io API key. It is sent as HTTP header x-access-token.
Forecast (outputForecast)CheckboxdisabledInclude forecast data in output: additionally queries the /forecast endpoint and serves output 2.

Obtaining an API key​

Register at openuv.io and generate an API key in the dashboard. OpenUV.io offers a free plan with a limited daily quota as well as paid plans with higher limits; the current conditions are listed on the provider's website.

Input​

The input serves exclusively as trigger. The node evaluates no property of the incoming message and does not copy it to the output.

PropertyTypeMeaning
msganyEvery message starts the fetch for all configured locations.

Output​

The node has two outputs. Both payloads are largely flat objects; only location is already structured as an NGSI-LD GeoProperty. They can be handed to the NGSI-LD Builder.

Output 1: UVIndexObservation​

One message per location with msg.topic = "uv-observation" and msg.location = location name.

{
"id": "urn:ngsi-ld:UVIndexObservation:Outdoor_Pool_Example_Town",
"type": "UVIndexObservation",
"uVIndex": 3.2,
"uVIndexMax": 4.1,
"uVIndexMaxTime": "2026-10-01T11:20:00.000Z",
"ozone": 298.5,
"ozoneTime": "2026-10-01T07:00:00.000Z",
"safeExposure": {"st1": 52, "st2": 62, "st3": 83, "st4": 104, "st5": 166, "st6": 311},
"sunInfo": {
"sun_times": {"sunrise": "2026-10-01T05:25:00.000Z", "sunset": "2026-10-01T16:58:00.000Z"},
"sun_position": {"azimuth": 0.42, "altitude": 0.61}
},
"location": {"type": "GeoProperty", "value": {"type": "Point", "coordinates": [7.77, 49.44]}},
"locationName": "Outdoor Pool Example Town"
}
FieldMeaning
idurn:ngsi-ld:UVIndexObservation: plus location name; all characters except letters and digits are replaced by _.
typeAlways UVIndexObservation.
uVIndexCurrent UV index (dimensionless, OpenUV uv).
uVIndexMaxDaily maximum of the UV index (OpenUV uv_max).
uVIndexMaxTimeTime of the daily maximum (OpenUV uv_max_time, ISO 8601).
ozoneOzone column in Dobson units (OpenUV ozone).
ozoneTimeTimestamp of the ozone measurement (OpenUV ozone_time).
safeExposureSafe exposure time in minutes per Fitzpatrick skin type st1 to st6 (OpenUV safe_exposure_time), passed through unchanged.
sunInfoSunrise and sunset times and sun position (OpenUV sun_info), passed through unchanged.
locationGeoProperty with GeoJSON point [longitude, latitude] of the location.
locationNameThe location name you assigned.

Output 2: UVIndexForecast​

Only with forecast enabled. One message per forecast point (hourly for the current day) with msg.topic = "uv-forecast" and msg.location = location name.

{
"id": "urn:ngsi-ld:UVIndexForecast:Outdoor_Pool_Example_Town",
"type": "UVIndexForecast",
"dateIssued": "2026-10-01T12:00:00.000Z",
"uVIndex": 3.8,
"sunPosition": {"azimuth": 0.55, "altitude": 0.58},
"location": {"type": "GeoProperty", "value": {"type": "Point", "coordinates": [7.77, 49.44]}},
"locationName": "Outdoor Pool Example Town"
}
FieldMeaning
idurn:ngsi-ld:UVIndexForecast: plus sanitised location name.
typeAlways UVIndexForecast.
dateIssuedTime the forecast value applies to (OpenUV uv_time).
uVIndexForecast UV index.
sunPositionSun position at the forecast time (OpenUV sun_position).
location, locationNameAs on output 1.

Example flow​

The package ships with an example flow. Import it in Node-RED via Menu → Import → Examples → @htag/node-red-contrib-hypertegrity-weather → openuv-io. Alternatively download openuv-io.json and import it via Menu → Import.

Step 1

Inject

An Inject node triggers the fetch manually or at a fixed interval.

Step 2

OpenUV Index

The node queries OpenUV.io for the configured locations. In the example, API key and location list are empty and have to be completed.

Step 3

Debug

Two Debug nodes show observation and forecast in the sidebar.

Show example flow as JSON
[
{"id": "tab1", "type": "tab", "label": "openuv-io", "disabled": false, "info": ""},
{"id": "inject1", "type": "inject", "z": "tab1", "name": "Go!", "props": [{"p": "payload"}, {"p": "topic", "vt": "str"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 100, "wires": [["uv1"]]},
{"id": "uv1", "type": "openuv", "z": "tab1", "name": "", "apiKey": "", "locations": [], "outputForecast": false, "x": 300, "y": 100, "wires": [["debug1"], ["debug2"]]},
{"id": "debug1", "type": "debug", "z": "tab1", "name": "Observation", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 490, "y": 80, "wires": []},
{"id": "debug2", "type": "debug", "z": "tab1", "name": "Forecast", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 480, "y": 120, "wires": []}
]

Status and errors​

IndicatorMeaning
Red ring "No API Key configured"The API key field is empty. The fetch is not started; the error is also reported via node.error.
Grey ring "No locations configured"The location list is empty. The fetch is not started.
Blue dot "Fetching…"The fetch is running.
Green dot "Active: n"All n locations were queried successfully.
Yellow dot "Active: n, Errors: m"n locations succeeded, m locations failed.
Red ring "Fetch Error"No location could be queried.
Error "Error fetching UV data (location): …"The request to /uv failed for this location, for example because of an invalid key, an exhausted quota or a timeout.
Warning "Error fetching forecast data (location): …"The request to /forecast failed; the observation was emitted anyway.

Errors are reported via node.error and can be handled with a Catch node.

Notes​

  • Quota and rate limits: OpenUV.io limits the number of requests per day depending on the plan. Every trigger causes one request per location, two with forecast enabled. With 5 locations and hourly polling this amounts to 120 or 240 requests per day. At night OpenUV.io returns a UV index of 0; save requests by triggering only between sunrise and sunset.
  • Terms of use: Use of the API is subject to the OpenUV.io terms. Check whether your plan permits publication of the data.
  • Context storage: The node stores no data in the Node-RED context.
  • Proxy: There is no separate proxy setting; axios honours the environment variables HTTP_PROXY and HTTPS_PROXY.
  • Units: UV index dimensionless on the WHO scale, ozone in Dobson units, safe exposure times in minutes, sun position in radians.
  • External services in the editor: The map loads tiles from OpenStreetMap; the browser running the Node-RED editor needs internet access for this.
  • Personal data: The output contains coordinates and the location names you assigned. Do not use names of private individuals or private addresses as location names if the data is published.
  • Data model: The output types UVIndexObservation and UVIndexForecast follow the conventions of the Smart Data Models (flat attributes, GeoProperty for location).

References​