diff --git a/.coveragerc b/.coveragerc index aa8f2d8c03d..25ee023ac84 100644 --- a/.coveragerc +++ b/.coveragerc @@ -19,6 +19,9 @@ omit = homeassistant/components/adguard/switch.py homeassistant/components/ads/* homeassistant/components/aftership/sensor.py + homeassistant/components/airly/__init__.py + homeassistant/components/airly/air_quality.py + homeassistant/components/airly/const.py homeassistant/components/airvisual/sensor.py homeassistant/components/aladdin_connect/cover.py homeassistant/components/alarm_control_panel/manual_mqtt.py diff --git a/CODEOWNERS b/CODEOWNERS index 418eb745ecb..8073020712d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,6 +14,7 @@ homeassistant/scripts/check_config.py @kellerza # Integrations homeassistant/components/adguard/* @frenck +homeassistant/components/airly/* @bieniu homeassistant/components/airvisual/* @bachya homeassistant/components/alarm_control_panel/* @colinodell homeassistant/components/alexa/* @home-assistant/cloud diff --git a/homeassistant/components/airly/__init__.py b/homeassistant/components/airly/__init__.py new file mode 100644 index 00000000000..56b3477ac89 --- /dev/null +++ b/homeassistant/components/airly/__init__.py @@ -0,0 +1,21 @@ +"""The Airly component.""" +from homeassistant.core import Config, HomeAssistant + + +async def async_setup(hass: HomeAssistant, config: Config) -> bool: + """Set up configured Airly.""" + return True + + +async def async_setup_entry(hass, config_entry): + """Set up Airly as config entry.""" + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(config_entry, "air_quality") + ) + return True + + +async def async_unload_entry(hass, config_entry): + """Unload a config entry.""" + await hass.config_entries.async_forward_entry_unload(config_entry, "air_quality") + return True diff --git a/homeassistant/components/airly/air_quality.py b/homeassistant/components/airly/air_quality.py new file mode 100644 index 00000000000..a8ec82ab304 --- /dev/null +++ b/homeassistant/components/airly/air_quality.py @@ -0,0 +1,201 @@ +"""Support for the Airly service.""" +import asyncio +import logging +from datetime import timedelta + +import async_timeout +from aiohttp.client_exceptions import ClientConnectorError +from airly import Airly +from airly.exceptions import AirlyError + +from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME +from homeassistant.components.air_quality import ( + AirQualityEntity, + ATTR_AQI, + ATTR_PM_10, + ATTR_PM_2_5, +) +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.util import Throttle + +from .const import NO_AIRLY_SENSORS + +_LOGGER = logging.getLogger(__name__) + +ATTRIBUTION = "Data provided by Airly" + +ATTR_API_ADVICE = "ADVICE" +ATTR_API_CAQI = "CAQI" +ATTR_API_CAQI_DESCRIPTION = "DESCRIPTION" +ATTR_API_CAQI_LEVEL = "LEVEL" +ATTR_API_PM10 = "PM10" +ATTR_API_PM10_LIMIT = "PM10_LIMIT" +ATTR_API_PM10_PERCENT = "PM10_PERCENT" +ATTR_API_PM25 = "PM25" +ATTR_API_PM25_LIMIT = "PM25_LIMIT" +ATTR_API_PM25_PERCENT = "PM25_PERCENT" + +LABEL_ADVICE = "advice" +LABEL_AQI_LEVEL = f"{ATTR_AQI}_level" +LABEL_PM_2_5_LIMIT = f"{ATTR_PM_2_5}_limit" +LABEL_PM_2_5_PERCENT = f"{ATTR_PM_2_5}_percent_of_limit" +LABEL_PM_10_LIMIT = f"{ATTR_PM_10}_limit" +LABEL_PM_10_PERCENT = f"{ATTR_PM_10}_percent_of_limit" + +DEFAULT_SCAN_INTERVAL = timedelta(minutes=10) + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Add a Airly entities from a config_entry.""" + api_key = config_entry.data[CONF_API_KEY] + name = config_entry.data[CONF_NAME] + latitude = config_entry.data[CONF_LATITUDE] + longitude = config_entry.data[CONF_LONGITUDE] + + websession = async_get_clientsession(hass) + + data = AirlyData(websession, api_key, latitude, longitude) + + async_add_entities([AirlyAirQuality(data, name)], True) + + +def round_state(func): + """Round state.""" + + def _decorator(self): + res = func(self) + if isinstance(res, float): + return round(res) + return res + + return _decorator + + +class AirlyAirQuality(AirQualityEntity): + """Define an Airly air_quality.""" + + def __init__(self, airly, name): + """Initialize.""" + self.airly = airly + self.data = airly.data + self._name = name + self._pm_2_5 = None + self._pm_10 = None + self._aqi = None + self._icon = "mdi:blur" + self._attrs = {} + + @property + def name(self): + """Return the name.""" + return self._name + + @property + def icon(self): + """Return the icon.""" + return self._icon + + @property + @round_state + def air_quality_index(self): + """Return the air quality index.""" + return self._aqi + + @property + @round_state + def particulate_matter_2_5(self): + """Return the particulate matter 2.5 level.""" + return self._pm_2_5 + + @property + @round_state + def particulate_matter_10(self): + """Return the particulate matter 10 level.""" + return self._pm_10 + + @property + def attribution(self): + """Return the attribution.""" + return ATTRIBUTION + + @property + def state(self): + """Return the CAQI description.""" + return self.data[ATTR_API_CAQI_DESCRIPTION] + + @property + def unique_id(self): + """Return a unique_id for this entity.""" + return f"{self.airly.latitude}-{self.airly.longitude}" + + @property + def available(self): + """Return True if entity is available.""" + return bool(self.airly.data) + + @property + def device_state_attributes(self): + """Return the state attributes.""" + self._attrs[LABEL_ADVICE] = self.data[ATTR_API_ADVICE] + self._attrs[LABEL_AQI_LEVEL] = self.data[ATTR_API_CAQI_LEVEL] + self._attrs[LABEL_PM_2_5_LIMIT] = self.data[ATTR_API_PM25_LIMIT] + self._attrs[LABEL_PM_2_5_PERCENT] = round(self.data[ATTR_API_PM25_PERCENT]) + self._attrs[LABEL_PM_10_LIMIT] = self.data[ATTR_API_PM10_LIMIT] + self._attrs[LABEL_PM_10_PERCENT] = round(self.data[ATTR_API_PM10_PERCENT]) + return self._attrs + + async def async_update(self): + """Get the data from Airly.""" + await self.airly.async_update() + + self._pm_10 = self.data[ATTR_API_PM10] + self._pm_2_5 = self.data[ATTR_API_PM25] + self._aqi = self.data[ATTR_API_CAQI] + + +class AirlyData: + """Define an object to hold sensor data.""" + + def __init__(self, session, api_key, latitude, longitude): + """Initialize.""" + self.latitude = latitude + self.longitude = longitude + self.airly = Airly(api_key, session) + self.data = {} + + @Throttle(DEFAULT_SCAN_INTERVAL) + async def async_update(self): + """Update Airly data.""" + + try: + with async_timeout.timeout(10): + measurements = self.airly.create_measurements_session_point( + self.latitude, self.longitude + ) + await measurements.update() + + values = measurements.current["values"] + index = measurements.current["indexes"][0] + standards = measurements.current["standards"] + + if index["description"] == NO_AIRLY_SENSORS: + _LOGGER.error("Can't retrieve data: no Airly sensors in this area") + return + for value in values: + self.data[value["name"]] = value["value"] + for standard in standards: + self.data[f"{standard['pollutant']}_LIMIT"] = standard["limit"] + self.data[f"{standard['pollutant']}_PERCENT"] = standard["percent"] + self.data[ATTR_API_CAQI] = index["value"] + self.data[ATTR_API_CAQI_LEVEL] = index["level"].lower().replace("_", " ") + self.data[ATTR_API_CAQI_DESCRIPTION] = index["description"] + self.data[ATTR_API_ADVICE] = index["advice"] + _LOGGER.debug("Data retrieved from Airly") + except ( + ValueError, + AirlyError, + asyncio.TimeoutError, + ClientConnectorError, + ) as error: + _LOGGER.error(error) + self.data = {} diff --git a/homeassistant/components/airly/config_flow.py b/homeassistant/components/airly/config_flow.py new file mode 100644 index 00000000000..b361930fa7d --- /dev/null +++ b/homeassistant/components/airly/config_flow.py @@ -0,0 +1,114 @@ +"""Adds config flow for Airly.""" +import async_timeout +import voluptuous as vol +from airly import Airly +from airly.exceptions import AirlyError + +import homeassistant.helpers.config_validation as cv +from homeassistant import config_entries +from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME +from homeassistant.core import callback +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import DEFAULT_NAME, DOMAIN, NO_AIRLY_SENSORS + + +@callback +def configured_instances(hass): + """Return a set of configured Airly instances.""" + return set( + entry.data[CONF_NAME] for entry in hass.config_entries.async_entries(DOMAIN) + ) + + +class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + """Config flow for Airly.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL + + def __init__(self): + """Initialize.""" + self._errors = {} + + async def async_step_user(self, user_input=None): + """Handle a flow initialized by the user.""" + self._errors = {} + + websession = async_get_clientsession(self.hass) + + if user_input is not None: + if user_input[CONF_NAME] in configured_instances(self.hass): + self._errors[CONF_NAME] = "name_exists" + api_key_valid = await self._test_api_key(websession, user_input["api_key"]) + if not api_key_valid: + self._errors["base"] = "auth" + else: + location_valid = await self._test_location( + websession, + user_input["api_key"], + user_input["latitude"], + user_input["longitude"], + ) + if not location_valid: + self._errors["base"] = "wrong_location" + + if not self._errors: + return self.async_create_entry( + title=user_input[CONF_NAME], data=user_input + ) + + return self._show_config_form( + name=DEFAULT_NAME, + api_key="", + latitude=self.hass.config.latitude, + longitude=self.hass.config.longitude, + ) + + def _show_config_form(self, name=None, api_key=None, latitude=None, longitude=None): + """Show the configuration form to edit data.""" + return self.async_show_form( + step_id="user", + data_schema=vol.Schema( + { + vol.Required(CONF_API_KEY, default=api_key): str, + vol.Optional( + CONF_LATITUDE, default=self.hass.config.latitude + ): cv.latitude, + vol.Optional( + CONF_LONGITUDE, default=self.hass.config.longitude + ): cv.longitude, + vol.Optional(CONF_NAME, default=name): str, + } + ), + errors=self._errors, + ) + + async def _test_api_key(self, client, api_key): + """Return true if api_key is valid.""" + + with async_timeout.timeout(10): + airly = Airly(api_key, client) + measurements = airly.create_measurements_session_point( + latitude=52.24131, longitude=20.99101 + ) + try: + await measurements.update() + except AirlyError: + return False + return True + + async def _test_location(self, client, api_key, latitude, longitude): + """Return true if location is valid.""" + + with async_timeout.timeout(10): + airly = Airly(api_key, client) + measurements = airly.create_measurements_session_point( + latitude=latitude, longitude=longitude + ) + + await measurements.update() + current = measurements.current + if current["indexes"][0]["description"] == NO_AIRLY_SENSORS: + return False + return True diff --git a/homeassistant/components/airly/const.py b/homeassistant/components/airly/const.py new file mode 100644 index 00000000000..5313ba0e494 --- /dev/null +++ b/homeassistant/components/airly/const.py @@ -0,0 +1,4 @@ +"""Constants for Airly integration.""" +DEFAULT_NAME = "Airly" +DOMAIN = "airly" +NO_AIRLY_SENSORS = "There are no Airly sensors in this area yet." diff --git a/homeassistant/components/airly/manifest.json b/homeassistant/components/airly/manifest.json new file mode 100644 index 00000000000..1859f084bf1 --- /dev/null +++ b/homeassistant/components/airly/manifest.json @@ -0,0 +1,9 @@ +{ + "domain": "airly", + "name": "Airly", + "documentation": "https://www.home-assistant.io/integrations/airly", + "dependencies": [], + "codeowners": ["@bieniu"], + "requirements": ["airly==0.0.2"], + "config_flow": true +} diff --git a/homeassistant/components/airly/strings.json b/homeassistant/components/airly/strings.json new file mode 100644 index 00000000000..116b6df83e6 --- /dev/null +++ b/homeassistant/components/airly/strings.json @@ -0,0 +1,22 @@ +{ + "config": { + "title": "Airly", + "step": { + "user": { + "title": "Airly", + "description": "Set up Airly air quality integration. To generate API key go to https://developer.airly.eu/register", + "data": { + "name": "Name of the integration", + "api_key": "Airly API key", + "latitude": "Latitude", + "longitude": "Longitude" + } + } + }, + "error": { + "name_exists": "Name already exists.", + "wrong_location": "No Airly measuring stations in this area.", + "auth": "API key is not correct." + } + } +} diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 21f57934e95..7557fc32b40 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -7,6 +7,7 @@ To update, run python3 -m script.hassfest FLOWS = [ "adguard", + "airly", "ambiclimate", "ambient_station", "axis", diff --git a/requirements_all.txt b/requirements_all.txt index 208eeac899b..5731158cd2f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -184,6 +184,9 @@ aiounifi==11 # homeassistant.components.wwlln aiowwlln==2.0.2 +# homeassistant.components.airly +airly==0.0.2 + # homeassistant.components.aladdin_connect aladdin_connect==0.3 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9bc9870be10..1e948ded8a7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -76,6 +76,9 @@ aiounifi==11 # homeassistant.components.wwlln aiowwlln==2.0.2 +# homeassistant.components.airly +airly==0.0.2 + # homeassistant.components.ambiclimate ambiclimate==0.2.1 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index e35a83bd24d..a6f2584e256 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -54,6 +54,7 @@ TEST_REQUIREMENTS = ( "aioswitcher", "aiounifi", "aiowwlln", + "airly", "ambiclimate", "androidtv", "apns2", diff --git a/tests/components/airly/__init__.py b/tests/components/airly/__init__.py new file mode 100644 index 00000000000..f31dfb7712d --- /dev/null +++ b/tests/components/airly/__init__.py @@ -0,0 +1 @@ +"""Tests for Airly.""" diff --git a/tests/components/airly/test_config_flow.py b/tests/components/airly/test_config_flow.py new file mode 100644 index 00000000000..8b615b34c2a --- /dev/null +++ b/tests/components/airly/test_config_flow.py @@ -0,0 +1,93 @@ +"""Define tests for the Airly config flow.""" +import json + +from airly.exceptions import AirlyError +from asynctest import patch + +from homeassistant import data_entry_flow +from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME +from homeassistant.components.airly import config_flow +from homeassistant.components.airly.const import DOMAIN + +from tests.common import load_fixture, MockConfigEntry + +CONFIG = { + CONF_NAME: "abcd", + CONF_API_KEY: "foo", + CONF_LATITUDE: 123, + CONF_LONGITUDE: 456, +} + + +async def test_show_form(hass): + """Test that the form is served with no input.""" + flow = config_flow.AirlyFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=None) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + +async def test_invalid_api_key(hass): + """Test that errors are shown when API key is invalid.""" + with patch( + "airly._private._RequestsHandler.get", + side_effect=AirlyError(403, {"message": "Invalid authentication credentials"}), + ): + flow = config_flow.AirlyFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=CONFIG) + + assert result["errors"] == {"base": "auth"} + + +async def test_invalid_location(hass): + """Test that errors are shown when location is invalid.""" + with patch( + "airly._private._RequestsHandler.get", + return_value=json.loads(load_fixture("airly_no_station.json")), + ): + flow = config_flow.AirlyFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=CONFIG) + + assert result["errors"] == {"base": "wrong_location"} + + +async def test_duplicate_error(hass): + """Test that errors are shown when duplicates are added.""" + + with patch( + "airly._private._RequestsHandler.get", + return_value=json.loads(load_fixture("airly_valid_station.json")), + ): + MockConfigEntry(domain=DOMAIN, data=CONFIG).add_to_hass(hass) + flow = config_flow.AirlyFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=CONFIG) + + assert result["errors"] == {CONF_NAME: "name_exists"} + + +async def test_create_entry(hass): + """Test that the user step works.""" + + with patch( + "airly._private._RequestsHandler.get", + return_value=json.loads(load_fixture("airly_valid_station.json")), + ): + flow = config_flow.AirlyFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=CONFIG) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == CONFIG[CONF_NAME] + assert result["data"][CONF_LATITUDE] == CONFIG[CONF_LATITUDE] + assert result["data"][CONF_LONGITUDE] == CONFIG[CONF_LONGITUDE] + assert result["data"][CONF_API_KEY] == CONFIG[CONF_API_KEY] diff --git a/tests/fixtures/airly_no_station.json b/tests/fixtures/airly_no_station.json new file mode 100644 index 00000000000..cc64934938f --- /dev/null +++ b/tests/fixtures/airly_no_station.json @@ -0,0 +1,642 @@ +{ + "current": { + "fromDateTime": "2019-10-02T05:53:00.608Z", + "tillDateTime": "2019-10-02T06:53:00.608Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, + "history": [{ + "fromDateTime": "2019-10-01T06:00:00.000Z", + "tillDateTime": "2019-10-01T07:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T07:00:00.000Z", + "tillDateTime": "2019-10-01T08:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T08:00:00.000Z", + "tillDateTime": "2019-10-01T09:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T09:00:00.000Z", + "tillDateTime": "2019-10-01T10:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T10:00:00.000Z", + "tillDateTime": "2019-10-01T11:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T11:00:00.000Z", + "tillDateTime": "2019-10-01T12:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T12:00:00.000Z", + "tillDateTime": "2019-10-01T13:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T13:00:00.000Z", + "tillDateTime": "2019-10-01T14:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T14:00:00.000Z", + "tillDateTime": "2019-10-01T15:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T15:00:00.000Z", + "tillDateTime": "2019-10-01T16:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T16:00:00.000Z", + "tillDateTime": "2019-10-01T17:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T17:00:00.000Z", + "tillDateTime": "2019-10-01T18:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T18:00:00.000Z", + "tillDateTime": "2019-10-01T19:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T19:00:00.000Z", + "tillDateTime": "2019-10-01T20:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T20:00:00.000Z", + "tillDateTime": "2019-10-01T21:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T21:00:00.000Z", + "tillDateTime": "2019-10-01T22:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T22:00:00.000Z", + "tillDateTime": "2019-10-01T23:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-01T23:00:00.000Z", + "tillDateTime": "2019-10-02T00:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T00:00:00.000Z", + "tillDateTime": "2019-10-02T01:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T01:00:00.000Z", + "tillDateTime": "2019-10-02T02:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T02:00:00.000Z", + "tillDateTime": "2019-10-02T03:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T03:00:00.000Z", + "tillDateTime": "2019-10-02T04:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T04:00:00.000Z", + "tillDateTime": "2019-10-02T05:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T05:00:00.000Z", + "tillDateTime": "2019-10-02T06:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }], + "forecast": [{ + "fromDateTime": "2019-10-02T06:00:00.000Z", + "tillDateTime": "2019-10-02T07:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T07:00:00.000Z", + "tillDateTime": "2019-10-02T08:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T08:00:00.000Z", + "tillDateTime": "2019-10-02T09:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T09:00:00.000Z", + "tillDateTime": "2019-10-02T10:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T10:00:00.000Z", + "tillDateTime": "2019-10-02T11:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T11:00:00.000Z", + "tillDateTime": "2019-10-02T12:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T12:00:00.000Z", + "tillDateTime": "2019-10-02T13:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T13:00:00.000Z", + "tillDateTime": "2019-10-02T14:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T14:00:00.000Z", + "tillDateTime": "2019-10-02T15:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T15:00:00.000Z", + "tillDateTime": "2019-10-02T16:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T16:00:00.000Z", + "tillDateTime": "2019-10-02T17:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T17:00:00.000Z", + "tillDateTime": "2019-10-02T18:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T18:00:00.000Z", + "tillDateTime": "2019-10-02T19:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T19:00:00.000Z", + "tillDateTime": "2019-10-02T20:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T20:00:00.000Z", + "tillDateTime": "2019-10-02T21:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T21:00:00.000Z", + "tillDateTime": "2019-10-02T22:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T22:00:00.000Z", + "tillDateTime": "2019-10-02T23:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-02T23:00:00.000Z", + "tillDateTime": "2019-10-03T00:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-03T00:00:00.000Z", + "tillDateTime": "2019-10-03T01:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-03T01:00:00.000Z", + "tillDateTime": "2019-10-03T02:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-03T02:00:00.000Z", + "tillDateTime": "2019-10-03T03:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-03T03:00:00.000Z", + "tillDateTime": "2019-10-03T04:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-03T04:00:00.000Z", + "tillDateTime": "2019-10-03T05:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }, { + "fromDateTime": "2019-10-03T05:00:00.000Z", + "tillDateTime": "2019-10-03T06:00:00.000Z", + "values": [], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": null, + "level": "UNKNOWN", + "description": "There are no Airly sensors in this area yet.", + "advice": null, + "color": "#999999" + }], + "standards": [] + }] +} \ No newline at end of file diff --git a/tests/fixtures/airly_valid_station.json b/tests/fixtures/airly_valid_station.json new file mode 100644 index 00000000000..656c62c04c2 --- /dev/null +++ b/tests/fixtures/airly_valid_station.json @@ -0,0 +1,1726 @@ +{ + "current": { + "fromDateTime": "2019-10-02T05:54:57.204Z", + "tillDateTime": "2019-10-02T06:54:57.204Z", + "values": [{ + "name": "PM1", + "value": 9.23 + }, { + "name": "PM25", + "value": 13.71 + }, { + "name": "PM10", + "value": 18.58 + }, { + "name": "PRESSURE", + "value": 1000.87 + }, { + "name": "HUMIDITY", + "value": 92.84 + }, { + "name": "TEMPERATURE", + "value": 14.23 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 22.85, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Great air!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 54.84 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 37.17 + }] + }, + "history": [{ + "fromDateTime": "2019-10-01T06:00:00.000Z", + "tillDateTime": "2019-10-01T07:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 5.95 + }, { + "name": "PM25", + "value": 8.54 + }, { + "name": "PM10", + "value": 11.46 + }, { + "name": "PRESSURE", + "value": 1009.61 + }, { + "name": "HUMIDITY", + "value": 97.6 + }, { + "name": "TEMPERATURE", + "value": 9.71 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 14.24, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Green equals clean!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 34.18 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 22.91 + }] + }, { + "fromDateTime": "2019-10-01T07:00:00.000Z", + "tillDateTime": "2019-10-01T08:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 4.2 + }, { + "name": "PM25", + "value": 5.88 + }, { + "name": "PM10", + "value": 7.88 + }, { + "name": "PRESSURE", + "value": 1009.13 + }, { + "name": "HUMIDITY", + "value": 90.84 + }, { + "name": "TEMPERATURE", + "value": 12.65 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 9.81, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Dear me, how wonderful!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 23.53 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 15.75 + }] + }, { + "fromDateTime": "2019-10-01T08:00:00.000Z", + "tillDateTime": "2019-10-01T09:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 3.63 + }, { + "name": "PM25", + "value": 5.56 + }, { + "name": "PM10", + "value": 7.71 + }, { + "name": "PRESSURE", + "value": 1008.27 + }, { + "name": "HUMIDITY", + "value": 84.61 + }, { + "name": "TEMPERATURE", + "value": 15.57 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 9.26, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Dear me, how wonderful!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 22.23 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 15.42 + }] + }, { + "fromDateTime": "2019-10-01T09:00:00.000Z", + "tillDateTime": "2019-10-01T10:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 2.9 + }, { + "name": "PM25", + "value": 3.93 + }, { + "name": "PM10", + "value": 5.24 + }, { + "name": "PRESSURE", + "value": 1007.57 + }, { + "name": "HUMIDITY", + "value": 79.52 + }, { + "name": "TEMPERATURE", + "value": 16.57 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 6.56, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe deep! The air is clean!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 15.74 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 10.48 + }] + }, { + "fromDateTime": "2019-10-01T10:00:00.000Z", + "tillDateTime": "2019-10-01T11:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 2.45 + }, { + "name": "PM25", + "value": 3.33 + }, { + "name": "PM10", + "value": 4.52 + }, { + "name": "PRESSURE", + "value": 1006.75 + }, { + "name": "HUMIDITY", + "value": 74.09 + }, { + "name": "TEMPERATURE", + "value": 16.95 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 5.55, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "The air is grand today. ;)", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 13.31 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 9.04 + }] + }, { + "fromDateTime": "2019-10-01T11:00:00.000Z", + "tillDateTime": "2019-10-01T12:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 2.0 + }, { + "name": "PM25", + "value": 2.93 + }, { + "name": "PM10", + "value": 3.98 + }, { + "name": "PRESSURE", + "value": 1005.71 + }, { + "name": "HUMIDITY", + "value": 69.06 + }, { + "name": "TEMPERATURE", + "value": 17.31 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 4.89, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Green equals clean!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 11.74 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 7.96 + }] + }, { + "fromDateTime": "2019-10-01T12:00:00.000Z", + "tillDateTime": "2019-10-01T13:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 1.92 + }, { + "name": "PM25", + "value": 2.69 + }, { + "name": "PM10", + "value": 3.68 + }, { + "name": "PRESSURE", + "value": 1005.03 + }, { + "name": "HUMIDITY", + "value": 65.08 + }, { + "name": "TEMPERATURE", + "value": 17.47 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 4.49, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Enjoy life!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 10.77 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 7.36 + }] + }, { + "fromDateTime": "2019-10-01T13:00:00.000Z", + "tillDateTime": "2019-10-01T14:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 1.79 + }, { + "name": "PM25", + "value": 2.57 + }, { + "name": "PM10", + "value": 3.53 + }, { + "name": "PRESSURE", + "value": 1004.26 + }, { + "name": "HUMIDITY", + "value": 63.72 + }, { + "name": "TEMPERATURE", + "value": 17.91 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 4.29, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Great air!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 10.29 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 7.06 + }] + }, { + "fromDateTime": "2019-10-01T14:00:00.000Z", + "tillDateTime": "2019-10-01T15:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 2.06 + }, { + "name": "PM25", + "value": 3.08 + }, { + "name": "PM10", + "value": 4.23 + }, { + "name": "PRESSURE", + "value": 1003.46 + }, { + "name": "HUMIDITY", + "value": 64.44 + }, { + "name": "TEMPERATURE", + "value": 17.84 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 5.14, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "The air is grand today. ;)", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 12.33 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 8.47 + }] + }, { + "fromDateTime": "2019-10-01T15:00:00.000Z", + "tillDateTime": "2019-10-01T16:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 3.17 + }, { + "name": "PM25", + "value": 4.61 + }, { + "name": "PM10", + "value": 6.25 + }, { + "name": "PRESSURE", + "value": 1003.18 + }, { + "name": "HUMIDITY", + "value": 65.32 + }, { + "name": "TEMPERATURE", + "value": 18.08 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 7.68, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Green, green, green!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 18.44 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 12.5 + }] + }, { + "fromDateTime": "2019-10-01T16:00:00.000Z", + "tillDateTime": "2019-10-01T17:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 4.17 + }, { + "name": "PM25", + "value": 5.91 + }, { + "name": "PM10", + "value": 8.06 + }, { + "name": "PRESSURE", + "value": 1003.05 + }, { + "name": "HUMIDITY", + "value": 66.14 + }, { + "name": "TEMPERATURE", + "value": 17.04 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 9.84, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Enjoy life!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 23.62 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 16.11 + }] + }, { + "fromDateTime": "2019-10-01T17:00:00.000Z", + "tillDateTime": "2019-10-01T18:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 6.4 + }, { + "name": "PM25", + "value": 10.93 + }, { + "name": "PM10", + "value": 15.7 + }, { + "name": "PRESSURE", + "value": 1002.85 + }, { + "name": "HUMIDITY", + "value": 68.31 + }, { + "name": "TEMPERATURE", + "value": 16.33 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 18.22, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "It couldn't be better ;)", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 43.74 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 31.4 + }] + }, { + "fromDateTime": "2019-10-01T18:00:00.000Z", + "tillDateTime": "2019-10-01T19:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 4.79 + }, { + "name": "PM25", + "value": 7.41 + }, { + "name": "PM10", + "value": 10.31 + }, { + "name": "PRESSURE", + "value": 1002.52 + }, { + "name": "HUMIDITY", + "value": 69.88 + }, { + "name": "TEMPERATURE", + "value": 15.98 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 12.35, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Enjoy life!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 29.65 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 20.63 + }] + }, { + "fromDateTime": "2019-10-01T19:00:00.000Z", + "tillDateTime": "2019-10-01T20:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 5.99 + }, { + "name": "PM25", + "value": 9.45 + }, { + "name": "PM10", + "value": 13.22 + }, { + "name": "PRESSURE", + "value": 1002.32 + }, { + "name": "HUMIDITY", + "value": 70.47 + }, { + "name": "TEMPERATURE", + "value": 15.76 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 15.74, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe deeply!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 37.78 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 26.44 + }] + }, { + "fromDateTime": "2019-10-01T20:00:00.000Z", + "tillDateTime": "2019-10-01T21:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 9.35 + }, { + "name": "PM25", + "value": 14.67 + }, { + "name": "PM10", + "value": 20.57 + }, { + "name": "PRESSURE", + "value": 1002.46 + }, { + "name": "HUMIDITY", + "value": 72.61 + }, { + "name": "TEMPERATURE", + "value": 15.47 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 24.45, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "It couldn't be better ;)", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 58.68 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 41.13 + }] + }, { + "fromDateTime": "2019-10-01T21:00:00.000Z", + "tillDateTime": "2019-10-01T22:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 9.95 + }, { + "name": "PM25", + "value": 15.37 + }, { + "name": "PM10", + "value": 21.33 + }, { + "name": "PRESSURE", + "value": 1002.59 + }, { + "name": "HUMIDITY", + "value": 75.09 + }, { + "name": "TEMPERATURE", + "value": 15.17 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 25.62, + "level": "LOW", + "description": "Air is quite good.", + "advice": "Take a breath!", + "color": "#D1CF1E" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 61.48 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 42.66 + }] + }, { + "fromDateTime": "2019-10-01T22:00:00.000Z", + "tillDateTime": "2019-10-01T23:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 10.16 + }, { + "name": "PM25", + "value": 15.78 + }, { + "name": "PM10", + "value": 21.97 + }, { + "name": "PRESSURE", + "value": 1002.59 + }, { + "name": "HUMIDITY", + "value": 77.68 + }, { + "name": "TEMPERATURE", + "value": 14.9 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 26.31, + "level": "LOW", + "description": "Air is quite good.", + "advice": "Great air for a walk to the park!", + "color": "#D1CF1E" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 63.14 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 43.93 + }] + }, { + "fromDateTime": "2019-10-01T23:00:00.000Z", + "tillDateTime": "2019-10-02T00:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 9.86 + }, { + "name": "PM25", + "value": 15.14 + }, { + "name": "PM10", + "value": 21.07 + }, { + "name": "PRESSURE", + "value": 1002.49 + }, { + "name": "HUMIDITY", + "value": 79.86 + }, { + "name": "TEMPERATURE", + "value": 14.56 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 25.24, + "level": "LOW", + "description": "Air is quite good.", + "advice": "Leave the mask at home today!", + "color": "#D1CF1E" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 60.57 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 42.14 + }] + }, { + "fromDateTime": "2019-10-02T00:00:00.000Z", + "tillDateTime": "2019-10-02T01:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 9.77 + }, { + "name": "PM25", + "value": 15.04 + }, { + "name": "PM10", + "value": 20.97 + }, { + "name": "PRESSURE", + "value": 1002.18 + }, { + "name": "HUMIDITY", + "value": 81.77 + }, { + "name": "TEMPERATURE", + "value": 14.13 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 25.07, + "level": "LOW", + "description": "Air is quite good.", + "advice": "Time for a walk with friends or activities with your family - because the air is clean!", + "color": "#D1CF1E" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 60.18 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 41.94 + }] + }, { + "fromDateTime": "2019-10-02T01:00:00.000Z", + "tillDateTime": "2019-10-02T02:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 9.67 + }, { + "name": "PM25", + "value": 14.9 + }, { + "name": "PM10", + "value": 20.67 + }, { + "name": "PRESSURE", + "value": 1002.01 + }, { + "name": "HUMIDITY", + "value": 84.5 + }, { + "name": "TEMPERATURE", + "value": 13.7 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 24.84, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Great air!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 59.62 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 41.33 + }] + }, { + "fromDateTime": "2019-10-02T02:00:00.000Z", + "tillDateTime": "2019-10-02T03:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 7.17 + }, { + "name": "PM25", + "value": 10.7 + }, { + "name": "PM10", + "value": 14.58 + }, { + "name": "PRESSURE", + "value": 1001.56 + }, { + "name": "HUMIDITY", + "value": 88.55 + }, { + "name": "TEMPERATURE", + "value": 13.44 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 17.83, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Catch your breath!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 42.8 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 29.17 + }] + }, { + "fromDateTime": "2019-10-02T03:00:00.000Z", + "tillDateTime": "2019-10-02T04:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 6.99 + }, { + "name": "PM25", + "value": 10.23 + }, { + "name": "PM10", + "value": 13.66 + }, { + "name": "PRESSURE", + "value": 1001.34 + }, { + "name": "HUMIDITY", + "value": 90.82 + }, { + "name": "TEMPERATURE", + "value": 13.3 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 17.05, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Perfect air for exercising! Go for it!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 40.91 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 27.33 + }] + }, { + "fromDateTime": "2019-10-02T04:00:00.000Z", + "tillDateTime": "2019-10-02T05:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 7.82 + }, { + "name": "PM25", + "value": 11.59 + }, { + "name": "PM10", + "value": 15.77 + }, { + "name": "PRESSURE", + "value": 1000.92 + }, { + "name": "HUMIDITY", + "value": 91.8 + }, { + "name": "TEMPERATURE", + "value": 13.34 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 19.32, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Dear me, how wonderful!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 46.36 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 31.54 + }] + }, { + "fromDateTime": "2019-10-02T05:00:00.000Z", + "tillDateTime": "2019-10-02T06:00:00.000Z", + "values": [{ + "name": "PM1", + "value": 10.16 + }, { + "name": "PM25", + "value": 15.35 + }, { + "name": "PM10", + "value": 21.45 + }, { + "name": "PRESSURE", + "value": 1000.82 + }, { + "name": "HUMIDITY", + "value": 92.15 + }, { + "name": "TEMPERATURE", + "value": 13.74 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 25.59, + "level": "LOW", + "description": "Air is quite good.", + "advice": "How about going for a walk?", + "color": "#D1CF1E" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 61.42 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 42.9 + }] + }], + "forecast": [{ + "fromDateTime": "2019-10-02T06:00:00.000Z", + "tillDateTime": "2019-10-02T07:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 13.28 + }, { + "name": "PM10", + "value": 18.37 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 22.14, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "It couldn't be better ;)", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 53.13 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 36.73 + }] + }, { + "fromDateTime": "2019-10-02T07:00:00.000Z", + "tillDateTime": "2019-10-02T08:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 11.19 + }, { + "name": "PM10", + "value": 15.65 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 18.65, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Enjoy life!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 44.76 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 31.31 + }] + }, { + "fromDateTime": "2019-10-02T08:00:00.000Z", + "tillDateTime": "2019-10-02T09:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 8.79 + }, { + "name": "PM10", + "value": 12.8 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 14.65, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe deep! The air is clean!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 35.15 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 25.59 + }] + }, { + "fromDateTime": "2019-10-02T09:00:00.000Z", + "tillDateTime": "2019-10-02T10:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 5.46 + }, { + "name": "PM10", + "value": 8.91 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 9.11, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe to fill your lungs!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 21.86 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 17.83 + }] + }, { + "fromDateTime": "2019-10-02T10:00:00.000Z", + "tillDateTime": "2019-10-02T11:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 2.26 + }, { + "name": "PM10", + "value": 5.02 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 5.02, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Enjoy life!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 9.06 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 10.05 + }] + }, { + "fromDateTime": "2019-10-02T11:00:00.000Z", + "tillDateTime": "2019-10-02T12:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 1.06 + }, { + "name": "PM10", + "value": 2.52 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 2.52, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "The air is great!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 4.22 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 5.05 + }] + }, { + "fromDateTime": "2019-10-02T12:00:00.000Z", + "tillDateTime": "2019-10-02T13:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 0.48 + }, { + "name": "PM10", + "value": 1.94 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 1.94, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe as much as you can!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 1.94 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 3.89 + }] + }, { + "fromDateTime": "2019-10-02T13:00:00.000Z", + "tillDateTime": "2019-10-02T14:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 0.63 + }, { + "name": "PM10", + "value": 2.26 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 2.26, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Enjoy life!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 2.53 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 4.52 + }] + }, { + "fromDateTime": "2019-10-02T14:00:00.000Z", + "tillDateTime": "2019-10-02T15:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 1.47 + }, { + "name": "PM10", + "value": 3.39 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 3.39, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe as much as you can!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 5.87 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 6.78 + }] + }, { + "fromDateTime": "2019-10-02T15:00:00.000Z", + "tillDateTime": "2019-10-02T16:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 2.62 + }, { + "name": "PM10", + "value": 5.02 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 5.02, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Great air!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 10.5 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 10.05 + }] + }, { + "fromDateTime": "2019-10-02T16:00:00.000Z", + "tillDateTime": "2019-10-02T17:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 3.89 + }, { + "name": "PM10", + "value": 8.02 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 8.02, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Dear me, how wonderful!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 15.56 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 16.04 + }] + }, { + "fromDateTime": "2019-10-02T17:00:00.000Z", + "tillDateTime": "2019-10-02T18:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 6.26 + }, { + "name": "PM10", + "value": 11.41 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 11.41, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "The air is great!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 25.05 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 22.83 + }] + }, { + "fromDateTime": "2019-10-02T18:00:00.000Z", + "tillDateTime": "2019-10-02T19:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 8.69 + }, { + "name": "PM10", + "value": 14.48 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 14.48, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Zero dust - zero worries!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 34.76 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 28.96 + }] + }, { + "fromDateTime": "2019-10-02T19:00:00.000Z", + "tillDateTime": "2019-10-02T20:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 10.78 + }, { + "name": "PM10", + "value": 16.86 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 17.97, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Zero dust - zero worries!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 43.13 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 33.72 + }] + }, { + "fromDateTime": "2019-10-02T20:00:00.000Z", + "tillDateTime": "2019-10-02T21:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 12.22 + }, { + "name": "PM10", + "value": 18.19 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 20.36, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe to fill your lungs!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 48.88 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 36.38 + }] + }, { + "fromDateTime": "2019-10-02T21:00:00.000Z", + "tillDateTime": "2019-10-02T22:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 13.06 + }, { + "name": "PM10", + "value": 18.62 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 21.77, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Dear me, how wonderful!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 52.25 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 37.24 + }] + }, { + "fromDateTime": "2019-10-02T22:00:00.000Z", + "tillDateTime": "2019-10-02T23:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 13.51 + }, { + "name": "PM10", + "value": 18.49 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 22.52, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "The air is great!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 54.06 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 36.98 + }] + }, { + "fromDateTime": "2019-10-02T23:00:00.000Z", + "tillDateTime": "2019-10-03T00:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 13.46 + }, { + "name": "PM10", + "value": 17.63 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 22.44, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Green, green, green!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 53.85 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 35.26 + }] + }, { + "fromDateTime": "2019-10-03T00:00:00.000Z", + "tillDateTime": "2019-10-03T01:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 13.05 + }, { + "name": "PM10", + "value": 16.36 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 21.74, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Catch your breath!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 52.19 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 32.73 + }] + }, { + "fromDateTime": "2019-10-03T01:00:00.000Z", + "tillDateTime": "2019-10-03T02:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 12.47 + }, { + "name": "PM10", + "value": 15.16 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 20.79, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Green, green, green!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 49.9 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 30.32 + }] + }, { + "fromDateTime": "2019-10-03T02:00:00.000Z", + "tillDateTime": "2019-10-03T03:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 11.99 + }, { + "name": "PM10", + "value": 14.07 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 19.98, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe as much as you can!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 47.94 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 28.14 + }] + }, { + "fromDateTime": "2019-10-03T03:00:00.000Z", + "tillDateTime": "2019-10-03T04:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 11.74 + }, { + "name": "PM10", + "value": 13.67 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 19.56, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Dear me, how wonderful!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 46.95 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 27.34 + }] + }, { + "fromDateTime": "2019-10-03T04:00:00.000Z", + "tillDateTime": "2019-10-03T05:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 11.44 + }, { + "name": "PM10", + "value": 13.51 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 19.06, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe to fill your lungs!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 45.74 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 27.02 + }] + }, { + "fromDateTime": "2019-10-03T05:00:00.000Z", + "tillDateTime": "2019-10-03T06:00:00.000Z", + "values": [{ + "name": "PM25", + "value": 10.88 + }, { + "name": "PM10", + "value": 13.38 + }], + "indexes": [{ + "name": "AIRLY_CAQI", + "value": 18.13, + "level": "VERY_LOW", + "description": "Great air here today!", + "advice": "Breathe as much as you can!", + "color": "#6BC926" + }], + "standards": [{ + "name": "WHO", + "pollutant": "PM25", + "limit": 25.0, + "percent": 43.52 + }, { + "name": "WHO", + "pollutant": "PM10", + "limit": 50.0, + "percent": 26.76 + }] + }] +} \ No newline at end of file