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:
- id: codespell
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"
- --quiet-level=2
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": {
"info": {
"arch": "CPU Architecture",

View file

@ -3,6 +3,7 @@ from __future__ import annotations
from collections import OrderedDict
from collections.abc import Callable, Sequence
from contextlib import suppress
import logging
import os
from pathlib import Path
@ -47,12 +48,19 @@ from .const import (
LEGACY_CONF_WHITELIST_EXTERNAL_DIRS,
__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 .helpers import (
config_per_platform,
config_validation as cv,
extract_domain_configs,
issue_registry as ir,
)
from .helpers.entity_values import EntityValues
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(
CUSTOMIZE_CONFIG_SCHEMA.extend(
{
@ -250,10 +279,10 @@ CORE_CONFIG_SCHEMA = vol.All(
],
_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_LEGACY_TEMPLATES): cv.boolean,
vol.Optional(CONF_CURRENCY): cv.currency,
vol.Optional(CONF_CURRENCY): _validate_currency,
}
),
_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.exceptions import TemplateError
from homeassistant.generated import currencies
from homeassistant.util import raise_if_invalid_path, slugify as util_slugify
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(
{
"AED",
"AFN",
"ALL",
"AMD",
"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",
currencies.ACTIVE_CURRENCIES, msg="invalid ISO 4217 formatted currency"
)
historic_currency = vol.In(
currencies.HISTORIC_CURRENCIES, msg="invalid ISO 4217 formatted historic 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"):
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__,
)
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
from homeassistant.helpers.entity import Entity
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
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."""
core_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))
== 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"}