Create repairs issue if an outdated currency code is configured (#81717)

* Create repairs issue if an outdated currency code is configured

* Add script for updating list of currencies

* Use black for formatting

* Move currency codes to a separate file

* Address review comments
This commit is contained in:
Erik Montnemery 2022-11-08 07:21:09 +01:00 committed by GitHub
parent 0ce301ae7b
commit c3d4a9cd99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 418 additions and 168 deletions

View File

@ -17,7 +17,7 @@ repos:
hooks: hooks:
- id: codespell - id: codespell
args: args:
- --ignore-words-list=hass,alot,datas,dof,dur,ether,farenheit,hist,iff,iif,ines,ist,lightsensor,mut,nd,pres,referer,rime,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort,ba,haa,pullrequests - --ignore-words-list=hass,alot,bre,datas,dof,dur,ether,farenheit,hist,iff,iif,ines,ist,lightsensor,mut,nd,pres,referer,rime,ser,serie,sur,te,technik,ue,uint,visability,wan,wanna,withing,iam,incomfort,ba,haa,pullrequests
- --skip="./.*,*.csv,*.json" - --skip="./.*,*.csv,*.json"
- --quiet-level=2 - --quiet-level=2
exclude_types: [csv, json] exclude_types: [csv, json]

View File

@ -1,4 +1,10 @@
{ {
"issues": {
"historic_currency": {
"title": "The configured currency is no longer in use",
"description": "The currency {currency} is no longer in use, please reconfigure the currency configuration."
}
},
"system_health": { "system_health": {
"info": { "info": {
"arch": "CPU Architecture", "arch": "CPU Architecture",

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from collections import OrderedDict from collections import OrderedDict
from collections.abc import Callable, Sequence from collections.abc import Callable, Sequence
from contextlib import suppress
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
@ -47,12 +48,19 @@ from .const import (
LEGACY_CONF_WHITELIST_EXTERNAL_DIRS, LEGACY_CONF_WHITELIST_EXTERNAL_DIRS,
__version__, __version__,
) )
from .core import DOMAIN as CONF_CORE, ConfigSource, HomeAssistant, callback from .core import (
DOMAIN as CONF_CORE,
ConfigSource,
HomeAssistant,
async_get_hass,
callback,
)
from .exceptions import HomeAssistantError from .exceptions import HomeAssistantError
from .helpers import ( from .helpers import (
config_per_platform, config_per_platform,
config_validation as cv, config_validation as cv,
extract_domain_configs, extract_domain_configs,
issue_registry as ir,
) )
from .helpers.entity_values import EntityValues from .helpers.entity_values import EntityValues
from .helpers.typing import ConfigType from .helpers.typing import ConfigType
@ -199,6 +207,27 @@ CUSTOMIZE_CONFIG_SCHEMA = vol.Schema(
} }
) )
def _validate_currency(data: Any) -> Any:
hass = async_get_hass()
try:
return cv.currency(data)
except vol.InInvalid:
with suppress(vol.InInvalid):
currency = cv.historic_currency(data)
ir.async_create_issue(
hass,
"homeassistant",
"historic_currency",
is_fixable=False,
severity=ir.IssueSeverity.WARNING,
translation_key="historic_currency",
translation_placeholders={"currency": currency},
)
return currency
raise
CORE_CONFIG_SCHEMA = vol.All( CORE_CONFIG_SCHEMA = vol.All(
CUSTOMIZE_CONFIG_SCHEMA.extend( CUSTOMIZE_CONFIG_SCHEMA.extend(
{ {
@ -250,10 +279,10 @@ CORE_CONFIG_SCHEMA = vol.All(
], ],
_no_duplicate_auth_mfa_module, _no_duplicate_auth_mfa_module,
), ),
# pylint: disable=no-value-for-parameter # pylint: disable-next=no-value-for-parameter
vol.Optional(CONF_MEDIA_DIRS): cv.schema_with_slug_keys(vol.IsDir()), vol.Optional(CONF_MEDIA_DIRS): cv.schema_with_slug_keys(vol.IsDir()),
vol.Optional(CONF_LEGACY_TEMPLATES): cv.boolean, vol.Optional(CONF_LEGACY_TEMPLATES): cv.boolean,
vol.Optional(CONF_CURRENCY): cv.currency, vol.Optional(CONF_CURRENCY): _validate_currency,
} }
), ),
_filter_bad_internal_external_urls, _filter_bad_internal_external_urls,

View File

@ -0,0 +1,290 @@
"""Automatically generated by currencies.py.
To update, run python3 -m script.currencies
"""
ACTIVE_CURRENCIES = {
"AED",
"AFN",
"ALL",
"AMD",
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BTN",
"BWP",
"BYN",
"BZD",
"CAD",
"CDF",
"CHF",
"CLP",
"CNY",
"COP",
"CRC",
"CUC",
"CUP",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EGP",
"ERN",
"ETB",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"INR",
"IQD",
"IRR",
"ISK",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KPW",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRU",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SDG",
"SEK",
"SGD",
"SHP",
"SLE",
"SLL",
"SOS",
"SRD",
"SSP",
"STN",
"SVC",
"SYP",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VED",
"VES",
"VND",
"VUV",
"WST",
"XAF",
"XCD",
"XOF",
"XPF",
"YER",
"ZAR",
"ZMW",
"ZWL",
}
HISTORIC_CURRENCIES = {
"ADP",
"AFA",
"ALK",
"AOK",
"AON",
"AOR",
"ARA",
"ARP",
"ARY",
"ATS",
"AYM",
"AZM",
"BAD",
"BEC",
"BEF",
"BEL",
"BGJ",
"BGK",
"BGL",
"BOP",
"BRB",
"BRC",
"BRE",
"BRN",
"BRR",
"BUK",
"BYB",
"BYR",
"CHC",
"CSD",
"CSJ",
"CSK",
"CYP",
"DDM",
"DEM",
"ECS",
"ECV",
"EEK",
"ESA",
"ESB",
"ESP",
"FIM",
"FRF",
"GEK",
"GHC",
"GHP",
"GNE",
"GNS",
"GQE",
"GRD",
"GWE",
"GWP",
"HRD",
"IEP",
"ILP",
"ILR",
"ISJ",
"ITL",
"LAJ",
"LSM",
"LTL",
"LTT",
"LUC",
"LUF",
"LUL",
"LVL",
"LVR",
"MGF",
"MLF",
"MRO",
"MTL",
"MTP",
"MVQ",
"MXP",
"MZE",
"MZM",
"NIC",
"NLG",
"PEH",
"PEI",
"PES",
"PLZ",
"PTE",
"RHD",
"ROK",
"ROL",
"RUR",
"SDD",
"SDP",
"SIT",
"SKK",
"SRG",
"STD",
"SUR",
"TJR",
"TMM",
"TPE",
"TRL",
"UAK",
"UGS",
"UGW",
"USS",
"UYN",
"UYP",
"VEB",
"VEF",
"VNC",
"XEU",
"XFO",
"YDD",
"YUD",
"YUM",
"YUN",
"ZAL",
"ZMK",
"ZRN",
"ZRZ",
"ZWC",
"ZWD",
"ZWN",
"ZWR",
}

View File

@ -88,6 +88,7 @@ from homeassistant.const import (
) )
from homeassistant.core import split_entity_id, valid_entity_id from homeassistant.core import split_entity_id, valid_entity_id
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
from homeassistant.generated import currencies
from homeassistant.util import raise_if_invalid_path, slugify as util_slugify from homeassistant.util import raise_if_invalid_path, slugify as util_slugify
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
@ -1654,167 +1655,10 @@ ACTION_TYPE_SCHEMAS: dict[str, Callable[[Any], dict]] = {
} }
# Validate currencies adopted by countries
currency = vol.In( currency = vol.In(
{ currencies.ACTIVE_CURRENCIES, msg="invalid ISO 4217 formatted currency"
"AED", )
"AFN",
"ALL", historic_currency = vol.In(
"AMD", currencies.HISTORIC_CURRENCIES, msg="invalid ISO 4217 formatted historic currency"
"ANG",
"AOA",
"ARS",
"AUD",
"AWG",
"AZN",
"BAM",
"BBD",
"BDT",
"BGN",
"BHD",
"BIF",
"BMD",
"BND",
"BOB",
"BRL",
"BSD",
"BTN",
"BWP",
"BYN",
"BYR",
"BZD",
"CAD",
"CDF",
"CHF",
"CLP",
"CNY",
"COP",
"CRC",
"CUP",
"CVE",
"CZK",
"DJF",
"DKK",
"DOP",
"DZD",
"EGP",
"ERN",
"ETB",
"EUR",
"FJD",
"FKP",
"GBP",
"GEL",
"GHS",
"GIP",
"GMD",
"GNF",
"GTQ",
"GYD",
"HKD",
"HNL",
"HRK",
"HTG",
"HUF",
"IDR",
"ILS",
"INR",
"IQD",
"IRR",
"ISK",
"JMD",
"JOD",
"JPY",
"KES",
"KGS",
"KHR",
"KMF",
"KPW",
"KRW",
"KWD",
"KYD",
"KZT",
"LAK",
"LBP",
"LKR",
"LRD",
"LSL",
"LTL",
"LYD",
"MAD",
"MDL",
"MGA",
"MKD",
"MMK",
"MNT",
"MOP",
"MRO",
"MUR",
"MVR",
"MWK",
"MXN",
"MYR",
"MZN",
"NAD",
"NGN",
"NIO",
"NOK",
"NPR",
"NZD",
"OMR",
"PAB",
"PEN",
"PGK",
"PHP",
"PKR",
"PLN",
"PYG",
"QAR",
"RON",
"RSD",
"RUB",
"RWF",
"SAR",
"SBD",
"SCR",
"SDG",
"SEK",
"SGD",
"SHP",
"SLL",
"SOS",
"SRD",
"SSP",
"STD",
"SYP",
"SZL",
"THB",
"TJS",
"TMT",
"TND",
"TOP",
"TRY",
"TTD",
"TWD",
"TZS",
"UAH",
"UGX",
"USD",
"UYU",
"UZS",
"VEF",
"VND",
"VUV",
"WST",
"XAF",
"XCD",
"XOF",
"XPF",
"YER",
"ZAR",
"ZMK",
"ZMW",
"ZWL",
},
msg="invalid ISO 4217 formatted currency",
) )

55
script/currencies.py Normal file
View File

@ -0,0 +1,55 @@
"""Helper script to update currency list from the official source."""
import pathlib
import black
from bs4 import BeautifulSoup
import requests
BASE = """
\"\"\"Automatically generated by currencies.py.
To update, run python3 -m script.currencies
\"\"\"
ACTIVE_CURRENCIES = {{ {} }}
HISTORIC_CURRENCIES = {{ {} }}
""".strip()
req = requests.get(
"https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml"
)
soup = BeautifulSoup(req.content, "xml")
active_currencies = sorted(
{
x.Ccy.contents[0]
for x in soup.ISO_4217.CcyTbl.children
if x.name == "CcyNtry"
and x.Ccy
and x.CcyMnrUnts.contents[0] != "N.A."
and "IsFund" not in x.CcyNm.attrs
and x.Ccy.contents[0] != "UYW"
}
)
req = requests.get(
"https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-three.xml"
)
soup = BeautifulSoup(req.content, "xml")
historic_currencies = sorted(
{
x.Ccy.contents[0]
for x in soup.ISO_4217.HstrcCcyTbl.children
if x.name == "HstrcCcyNtry"
and x.Ccy
and "IsFund" not in x.CcyNm.attrs
and x.Ccy.contents[0] not in active_currencies
}
)
pathlib.Path("homeassistant/generated/currencies.py").write_text(
black.format_str(
BASE.format(repr(active_currencies)[1:-1], repr(historic_currencies)[1:-1]),
mode=black.Mode(),
)
)

View File

@ -1332,3 +1332,15 @@ def test_currency():
for value in ("EUR", "USD"): for value in ("EUR", "USD"):
assert schema(value) assert schema(value)
def test_historic_currency():
"""Test historic currency validator."""
schema = vol.Schema(cv.historic_currency)
for value in (None, "BTC", "EUR"):
with pytest.raises(vol.MultipleInvalid):
schema(value)
for value in ("DEM", "NLG"):
assert schema(value)

View File

@ -28,7 +28,7 @@ from homeassistant.const import (
__version__, __version__,
) )
from homeassistant.core import ConfigSource, HomeAssistant, HomeAssistantError from homeassistant.core import ConfigSource, HomeAssistant, HomeAssistantError
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv, issue_registry as ir
import homeassistant.helpers.check_config as check_config import homeassistant.helpers.check_config as check_config
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.loader import async_get_integration from homeassistant.loader import async_get_integration
@ -445,7 +445,7 @@ async def test_loading_configuration_from_storage_with_yaml_only(hass, hass_stor
assert hass.config.config_source is ConfigSource.STORAGE assert hass.config.config_source is ConfigSource.STORAGE
async def test_igration_and_updating_configuration(hass, hass_storage): async def test_migration_and_updating_configuration(hass, hass_storage):
"""Test updating configuration stores the new configuration.""" """Test updating configuration stores the new configuration."""
core_data = { core_data = {
"data": { "data": {
@ -1205,3 +1205,17 @@ def test_identify_config_schema(domain, schema, expected):
config_util._identify_config_schema(Mock(DOMAIN=domain, CONFIG_SCHEMA=schema)) config_util._identify_config_schema(Mock(DOMAIN=domain, CONFIG_SCHEMA=schema))
== expected == expected
) )
def test_core_config_schema_historic_currency(hass):
"""Test core config schema."""
config_util.CORE_CONFIG_SCHEMA(
{
"currency": "LTT",
}
)
issue_registry = ir.async_get(hass)
issue = issue_registry.async_get_issue("homeassistant", "historic_currency")
assert issue
assert issue.translation_placeholders == {"currency": "LTT"}