Skip to content

Commit

Permalink
Merge pull request #9 from pawkakol1/new-feature/station-id-support
Browse files Browse the repository at this point in the history
New feature/station id support
  • Loading branch information
pawkakol1 authored Jun 6, 2022
2 parents 3ac5214 + 84349f0 commit e592d24
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 75 deletions.
47 changes: 33 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,46 @@ Integration supports below sensors of WAQI station:
- Rain
- Wind speed

It use geolocalized coordinates of station only.
Diffrent stations support diffrent data, "World's Air Quality Index" integration will recognise all parameters (availible in station) according to list of integration's supported sensors.

There are 2 supported integration methods:

- using geolocalized coordinates,
- using station ID.

Notice: waqi.info API supports stations with IDs between 1 and 13837 only. If your station has ID greater than 13837 it won't be able to add using station ID method nor geolocalized coordinates method. waqi.info website shows (on map) much more stations, than are supported by waqi.info API. All others stations are integrated with WAQI map from others websites. In the future waqi.info API would be developed, and there would be more supported stations, you can always check if your station is supported. You just need to copy below link, paste to the URL of web browser, change number of interested station, and paste your token insted {{token}}

`https://api.waqi.info/feed/@13837/?token={{token}}`

Web browser will receive some data, if station is supported or "Unknown ID" message, if it doesn't.

# Installation

Copy worlds_air_quality_index folder into /config/custom_components of Home Assistant instance.
You can also use HACS to install this repository.
Use HACS to install this repository.
You can also copy worlds_air_quality_index folder into /config/custom_components of Home Assistant instance, then restart HA.

# Adding Integration

To add integration use "Add Integration" of Home Assistant UI, and choose "World's Air Quality Index".
In popup window put:
To add integration use "Add Integration" button in section Settings->Devices&Services section, and choose "World's Air Quality Index".
In popup window choose method of station adding:

- your waqi.info account token (required)
- your own name of station (optional)
- latitude of WAQI station (optional)
- longitude of WAQI station (optional)
- using geographic localization,
- using station ID.

To get WAQI token you need to sign up on waqi.info.
If you won't put geolocalized coordinates of station, it will take your home coordinates, and it will find the clostest station.
If you won't put your own name it will take name of found station.
In case of geographic localization, there will be shown next window, where you need to put:

You can add more than 1 station.
- your waqi.info account token (required),
- latitude of WAQI station (required),
- longitude of WAQI station (required),
- your own name of station (optional).

In case of station ID, there will be shown next window, where you need to put:

- your waqi.info account token (required),
- ID of WAQI station (required),
- your own name of station (optional).

Diffrent stations support diffrent data, "World's Air Quality Index" integration will recognise all parameters according to list of integration's supported sensors.
To get WAQI token you need to sign up [here](https://aqicn.org/data-platform/token/).
As a default your home coordinates (set in HA) are putten in latitude and longitude fields in geographic localization method. This integration will find the clostest station, what is supported by waqi.info API.
If you won't put your own name it will take name of found station.
You can add more than 1 station.
158 changes: 129 additions & 29 deletions custom_components/worlds_air_quality_index/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,23 @@
CONF_NAME,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_TOKEN
CONF_TOKEN,
CONF_LOCATION,
CONF_METHOD,
CONF_ID
)
from .const import (
DOMAIN,
DEFAULT_NAME
)

DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_TOKEN): cv.string,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_LATITUDE): cv.string,
vol.Optional(CONF_LONGITUDE): cv.string,
}
DEFAULT_NAME,
GEOGRAPHIC_LOCALIZATION,
STATION_ID
)


class WorldsAirQualityIndexConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for worlds_air_quality_index integration."""

VERSION = 1
VERSION = 2

async def async_step_import(self, config: dict[str, Any]) -> FlowResult:
"""Import a configuration from config.yaml."""
Expand All @@ -45,47 +41,151 @@ async def async_step_import(self, config: dict[str, Any]) -> FlowResult:
config[CONF_NAME] = name
return await self.async_step_user(user_input=config)

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
async def async_step_user(self, user_input: dict[str, Any] | None = None) -> FlowResult:
"""Handle the initial step."""

data_schema = vol.Schema(
{
vol.Required(CONF_METHOD, default=GEOGRAPHIC_LOCALIZATION): vol.In(
(
GEOGRAPHIC_LOCALIZATION,
STATION_ID
)
)
}
)

if user_input is None:
return self.async_show_form(
step_id="user",
data_schema=data_schema,
)

if user_input[CONF_METHOD] == GEOGRAPHIC_LOCALIZATION:
return await self.async_step_geographic_localization()
return await self.async_step_station_id()

async def async_step_geographic_localization(self, user_input=None) -> FlowResult:
"""Handle the geographic localization step."""
errors = {}

if user_input:
data_schema = vol.Schema(
{
vol.Required(CONF_TOKEN): cv.string,
vol.Required(CONF_LATITUDE, default=self.hass.config.latitude): cv.latitude,
vol.Required(CONF_LONGITUDE, default=self.hass.config.longitude): cv.longitude,
vol.Optional(CONF_NAME): cv.string
}
)

if user_input:
token = user_input[CONF_TOKEN]
latitude = user_input.get(CONF_LATITUDE, self.hass.config.latitude)
longitude = user_input.get(CONF_LONGITUDE, self.hass.config.longitude)
requester = WaqiDataRequester(latitude, longitude, token)
latitude = user_input[CONF_LATITUDE]
longitude = user_input[CONF_LONGITUDE]
method = CONF_LOCATION
requester = WaqiDataRequester(latitude, longitude, token, None, method)
await self.hass.async_add_executor_job(requester.update)

testData = requester.GetData()
validateData = requester.GetData()
if validateData:
if validateData["status"] == "ok":
if "status" in validateData["data"]:
if validateData["data"]["status"] == "error":
if validateData["data"]["msg"] == "Unknown ID":
errors["base"] = "unknow_station_id"
else:
errors["base"] = "server_error"
elif validateData["status"] == "error":
if validateData["data"] == "Invalid key":
errors["base"] = "invalid_token"
else:
errors["base"] = "server_error"
else:
errors["base"] = "server_error"
else:
errors["base"] = "server_not_available"

stationName = requester.GetStationName()
name = user_input.get(CONF_NAME, stationName)

if testData is None:
errors["base"] = "invalid_token"
elif stationName is None:
errors["base"] = "invalid_station_name"
else:
if not errors:
await self.async_set_unique_id(name)
self._abort_if_unique_id_configured()

return self.async_create_entry(
title=name,
data={
CONF_NAME: name,
CONF_TOKEN: token,
CONF_LATITUDE: latitude,
CONF_LONGITUDE: longitude,
CONF_NAME: name,
CONF_METHOD: method,
},
)

return self.async_show_form(
step_id="user",
data_schema=DATA_SCHEMA,
step_id="geographic_localization",
data_schema=data_schema,
errors=errors,
)


async def async_step_station_id(self, user_input=None) -> FlowResult:
errors = {}

data_schema = vol.Schema(
{
vol.Required(CONF_TOKEN): cv.string,
vol.Required(CONF_ID): cv.string,
vol.Optional(CONF_NAME): cv.string
}
)

if user_input:

token = user_input[CONF_TOKEN]
id = user_input[CONF_ID]
method = CONF_ID
requester = WaqiDataRequester(None, None, token, id, method)
await self.hass.async_add_executor_job(requester.update)

validateData = requester.GetData()
if validateData:
if validateData["status"] == "ok":
if "status" in validateData["data"]:
if validateData["data"]["status"] == "error":
if validateData["data"]["msg"] == "Unknown ID":
errors["base"] = "unknow_station_id"
else:
errors["base"] = "server_error"
elif validateData["status"] == "error":
if validateData["data"] == "Invalid key":
errors["base"] = "invalid_token"
else:
errors["base"] = "server_error"
else:
errors["base"] = "server_error"
else:
errors["base"] = "server_not_available"

stationName = requester.GetStationName()
name = user_input.get(CONF_NAME, stationName)

if not errors:
await self.async_set_unique_id(name)
self._abort_if_unique_id_configured()

return self.async_create_entry(
title=name,
data={
CONF_TOKEN: token,
CONF_ID: id,
CONF_NAME: name,
CONF_METHOD: method,
},
)

return self.async_show_form(
step_id="station_id",
data_schema=data_schema,
errors=errors,
)
5 changes: 4 additions & 1 deletion custom_components/worlds_air_quality_index/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@

DOMAIN = "worlds_air_quality_index"
PLATFORMS = [Platform.SENSOR]
SW_VERSION = "0.2.0"
SW_VERSION = "0.3.0"

SCAN_INTERVAL = timedelta(minutes=30)

DISCOVERY_TYPE = "discovery_type"
GEOGRAPHIC_LOCALIZATION = "Geographic localization"
STATION_ID = "Station ID"
DEFAULT_NAME = 'waqi1'

SENSORS = {
Expand Down
2 changes: 1 addition & 1 deletion custom_components/worlds_air_quality_index/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
"dependencies": [],
"codeowners": ["@pawkakol1"],
"iot_class": "cloud_polling",
"version": "0.2.0"
"version": "0.3.0"
}
32 changes: 24 additions & 8 deletions custom_components/worlds_air_quality_index/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
CONF_NAME,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_TOKEN
CONF_TOKEN,
CONF_ID,
CONF_METHOD
)

from .const import (
Expand Down Expand Up @@ -67,29 +69,43 @@ async def async_setup_platform(
)
)


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the world_air_quality_index sensor entry."""

_LOGGER.debug("config token:")
_LOGGER.debug(entry.data[CONF_TOKEN])
_LOGGER.debug(entry.data[CONF_LATITUDE])
_LOGGER.debug(entry.data[CONF_LONGITUDE])
_LOGGER.debug("config method:")
_LOGGER.debug(entry.data[CONF_METHOD])
_LOGGER.debug("config name:")
_LOGGER.debug(entry.data[CONF_NAME])

name = entry.data[CONF_NAME]
token = entry.data[CONF_TOKEN]
latitude = entry.data[CONF_LATITUDE]
longitude = entry.data[CONF_LONGITUDE]
requester = WaqiDataRequester(latitude, longitude, token)
method = entry.data[CONF_METHOD]

if method == CONF_ID:
_LOGGER.debug("config ID:")
_LOGGER.debug(entry.data[CONF_ID])
id = entry.data[CONF_ID]
requester = WaqiDataRequester(None, None, token, id, method)
else:
_LOGGER.debug("config latitude:")
_LOGGER.debug(entry.data[CONF_LATITUDE])
_LOGGER.debug("config longitude:")
_LOGGER.debug(entry.data[CONF_LONGITUDE])
latitude = entry.data[CONF_LATITUDE]
longitude = entry.data[CONF_LONGITUDE]
requester = WaqiDataRequester(latitude, longitude, token, None, method)

await hass.async_add_executor_job(requester.update)

scannedData = requester.GetData()
scannedData = scannedData["data"]["iaqi"]

entities = []
#entities.append(WorldsAirQualityIndexAqiSensor(requester))

for res in SENSORS:
if res == "aqi" or res in scannedData:
entities.append(WorldsAirQualityIndexSensor(res, requester))
Expand Down
38 changes: 30 additions & 8 deletions custom_components/worlds_air_quality_index/strings.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
{
"config": {
"error": {
"invalid_token": "Invalid token",
"unknow_station_id": "WAQI API doesn't support this station ID",
"server_error": "Server error",
"server_not_available": "Server is not available",
"invalid_station_name": "Invalid station name"
},
"step": {
"user": {
"title": "Choose station adding method WAQI",
"description": "Choose station adding method WAQI, what you preffered: using geographic localization or using station ID",
"data": {
"name": "Name of service",
"token": "Token key of World's Air Quality Index account",
"latitude": "Latitude of station or site",
"longitude": "Longitude of station or site",
"geographic_localization": "Geographic localization",
"station_id": "Station ID",
"scan_interval": "Scan interval of station updating"
}
},
"geographic_localization": {
"title": "Add WAQI station using grographic localization",
"description": "Fill your WAQI token, latitude and longitude of your station or home, and optionally custom name for service instace. You can find WAQI token here https://aqicn.org/data-platform/token/",
"data": {
"token": "Token key of World's Air Quality Index account",
"latitude": "Latitude of station",
"longitude": "Longitude of station",
"name": "Name of service"
}
},
"station_id": {
"title": "Add WAQI station using ID",
"description": "Fill your WAQI token, id of your station (without @ prefix) and optionally custom name for service instace. You can find WAQI token here https://aqicn.org/data-platform/token/",
"data": {
"token": "Token key of World's Air Quality Index account",
"id": "Station ID",
"name": "Name of service"
}
}
},
"error": {
"invalid_token": "Invalid token",
"invalid_station_name": "Invalid station name"
}
}
}
Loading

0 comments on commit e592d24

Please sign in to comment.