From e0be4efe3eb99fb731b622268dfa11e7d7cd22d4 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 7 Aug 2019 02:32:15 +0200 Subject: [PATCH] Add test case to identify missing MQTT configuration abbreviations (#25616) * Add missing abbreviations * Move abbreviations to own file, add script to find missing abbreviations * Move check from script to test case * Lint * Rewrite to use pathlib * Lint --- .../components/mqtt/abbreviations.py | 180 ++++++++++++++++++ homeassistant/components/mqtt/discovery.py | 145 +------------- tests/components/mqtt/test_discovery.py | 60 ++++++ 3 files changed, 241 insertions(+), 144 deletions(-) create mode 100644 homeassistant/components/mqtt/abbreviations.py diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py new file mode 100644 index 00000000000..2350dfc6634 --- /dev/null +++ b/homeassistant/components/mqtt/abbreviations.py @@ -0,0 +1,180 @@ +"""Abbreviations supported by MQTT discovery.""" + +ABBREVIATIONS = { + "act_t": "action_topic", + "act_tpl": "action_template", + "aux_cmd_t": "aux_command_topic", + "aux_stat_tpl": "aux_state_template", + "aux_stat_t": "aux_state_topic", + "avty_t": "availability_topic", + "away_mode_cmd_t": "away_mode_command_topic", + "away_mode_stat_tpl": "away_mode_state_template", + "away_mode_stat_t": "away_mode_state_topic", + "b_tpl": "blue_template", + "bri_cmd_t": "brightness_command_topic", + "bri_scl": "brightness_scale", + "bri_stat_t": "brightness_state_topic", + "bri_tpl": "brightness_template", + "bri_val_tpl": "brightness_value_template", + "clr_temp_cmd_tpl": "color_temp_command_template", + "bat_lev_t": "battery_level_topic", + "bat_lev_tpl": "battery_level_template", + "chrg_t": "charging_topic", + "chrg_tpl": "charging_template", + "clr_temp_cmd_t": "color_temp_command_topic", + "clr_temp_stat_t": "color_temp_state_topic", + "clr_temp_tpl": "color_temp_template", + "clr_temp_val_tpl": "color_temp_value_template", + "cln_t": "cleaning_topic", + "cln_tpl": "cleaning_template", + "cmd_off_tpl": "command_off_template", + "cmd_on_tpl": "command_on_template", + "cmd_t": "command_topic", + "cmd_tpl": "command_template", + "cod_arm_req": "code_arm_required", + "cod_dis_req": "code_disarm_required", + "curr_temp_t": "current_temperature_topic", + "curr_temp_tpl": "current_temperature_template", + "dev": "device", + "dev_cla": "device_class", + "dock_t": "docked_topic", + "dock_tpl": "docked_template", + "err_t": "error_topic", + "err_tpl": "error_template", + "fanspd_t": "fan_speed_topic", + "fanspd_tpl": "fan_speed_template", + "fanspd_lst": "fan_speed_list", + "flsh_tlng": "flash_time_long", + "flsh_tsht": "flash_time_short", + "fx_cmd_t": "effect_command_topic", + "fx_list": "effect_list", + "fx_stat_t": "effect_state_topic", + "fx_tpl": "effect_template", + "fx_val_tpl": "effect_value_template", + "exp_aft": "expire_after", + "fan_mode_cmd_t": "fan_mode_command_topic", + "fan_mode_stat_tpl": "fan_mode_state_template", + "fan_mode_stat_t": "fan_mode_state_topic", + "frc_upd": "force_update", + "g_tpl": "green_template", + "hold_cmd_t": "hold_command_topic", + "hold_stat_tpl": "hold_state_template", + "hold_stat_t": "hold_state_topic", + "hs_cmd_t": "hs_command_topic", + "hs_stat_t": "hs_state_topic", + "hs_val_tpl": "hs_value_template", + "ic": "icon", + "init": "initial", + "json_attr": "json_attributes", + "json_attr_t": "json_attributes_topic", + "json_attr_tpl": "json_attributes_template", + "max_temp": "max_temp", + "min_temp": "min_temp", + "mode_cmd_t": "mode_command_topic", + "mode_stat_tpl": "mode_state_template", + "mode_stat_t": "mode_state_topic", + "name": "name", + "off_dly": "off_delay", + "on_cmd_type": "on_command_type", + "opt": "optimistic", + "osc_cmd_t": "oscillation_command_topic", + "osc_stat_t": "oscillation_state_topic", + "osc_val_tpl": "oscillation_value_template", + "pl_arm_away": "payload_arm_away", + "pl_arm_home": "payload_arm_home", + "pl_arm_nite": "payload_arm_night", + "pl_avail": "payload_available", + "pl_cln_sp": "payload_clean_spot", + "pl_cls": "payload_close", + "pl_disarm": "payload_disarm", + "pl_hi_spd": "payload_high_speed", + "pl_lock": "payload_lock", + "pl_loc": "payload_locate", + "pl_lo_spd": "payload_low_speed", + "pl_med_spd": "payload_medium_speed", + "pl_not_avail": "payload_not_available", + "pl_off": "payload_off", + "pl_off_spd": "payload_off_speed", + "pl_on": "payload_on", + "pl_open": "payload_open", + "pl_osc_off": "payload_oscillation_off", + "pl_osc_on": "payload_oscillation_on", + "pl_paus": "payload_pause", + "pl_stop": "payload_stop", + "pl_strt": "payload_start", + "pl_stpa": "payload_start_pause", + "pl_ret": "payload_return_to_base", + "pl_toff": "payload_turn_off", + "pl_ton": "payload_turn_on", + "pl_unlk": "payload_unlock", + "pos_clsd": "position_closed", + "pos_open": "position_open", + "pow_cmd_t": "power_command_topic", + "pow_stat_t": "power_state_topic", + "pow_stat_tpl": "power_state_template", + "r_tpl": "red_template", + "ret": "retain", + "rgb_cmd_tpl": "rgb_command_template", + "rgb_cmd_t": "rgb_command_topic", + "rgb_stat_t": "rgb_state_topic", + "rgb_val_tpl": "rgb_value_template", + "send_cmd_t": "send_command_topic", + "send_if_off": "send_if_off", + "set_fan_spd_t": "set_fan_speed_topic", + "set_pos_tpl": "set_position_template", + "set_pos_t": "set_position_topic", + "pos_t": "position_topic", + "spd_cmd_t": "speed_command_topic", + "spd_stat_t": "speed_state_topic", + "spd_val_tpl": "speed_value_template", + "spds": "speeds", + "stat_clsd": "state_closed", + "stat_off": "state_off", + "stat_on": "state_on", + "stat_open": "state_open", + "stat_t": "state_topic", + "stat_tpl": "state_template", + "stat_val_tpl": "state_value_template", + "sup_feat": "supported_features", + "swing_mode_cmd_t": "swing_mode_command_topic", + "swing_mode_stat_tpl": "swing_mode_state_template", + "swing_mode_stat_t": "swing_mode_state_topic", + "temp_cmd_t": "temperature_command_topic", + "temp_hi_cmd_t": "temperature_high_command_topic", + "temp_hi_stat_tpl": "temperature_high_state_template", + "temp_hi_stat_t": "temperature_high_state_topic", + "temp_lo_cmd_t": "temperature_low_command_topic", + "temp_lo_stat_tpl": "temperature_low_state_template", + "temp_lo_stat_t": "temperature_low_state_topic", + "temp_stat_tpl": "temperature_state_template", + "temp_stat_t": "temperature_state_topic", + "tilt_clsd_val": "tilt_closed_value", + "tilt_cmd_t": "tilt_command_topic", + "tilt_inv_stat": "tilt_invert_state", + "tilt_max": "tilt_max", + "tilt_min": "tilt_min", + "tilt_opnd_val": "tilt_opened_value", + "tilt_opt": "tilt_optimistic", + "tilt_status_t": "tilt_status_topic", + "tilt_status_tpl": "tilt_status_template", + "t": "topic", + "uniq_id": "unique_id", + "unit_of_meas": "unit_of_measurement", + "val_tpl": "value_template", + "whit_val_cmd_t": "white_value_command_topic", + "whit_val_scl": "white_value_scale", + "whit_val_stat_t": "white_value_state_topic", + "whit_val_tpl": "white_value_template", + "xy_cmd_t": "xy_command_topic", + "xy_stat_t": "xy_state_topic", + "xy_val_tpl": "xy_value_template", +} + +DEVICE_ABBREVIATIONS = { + "cns": "connections", + "ids": "identifiers", + "name": "name", + "mf": "manufacturer", + "mdl": "model", + "sw": "sw_version", +} diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index 1d307599698..d611b8db13e 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -10,6 +10,7 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.typing import HomeAssistantType +from .abbreviations import ABBREVIATIONS, DEVICE_ABBREVIATIONS from .const import ATTR_DISCOVERY_HASH, CONF_STATE_TOPIC _LOGGER = logging.getLogger(__name__) @@ -64,150 +65,6 @@ MQTT_DISCOVERY_NEW = "mqtt_discovery_new_{}_{}" TOPIC_BASE = "~" -ABBREVIATIONS = { - "aux_cmd_t": "aux_command_topic", - "aux_stat_tpl": "aux_state_template", - "aux_stat_t": "aux_state_topic", - "avty_t": "availability_topic", - "away_mode_cmd_t": "away_mode_command_topic", - "away_mode_stat_tpl": "away_mode_state_template", - "away_mode_stat_t": "away_mode_state_topic", - "b_tpl": "blue_template", - "bri_cmd_t": "brightness_command_topic", - "bri_scl": "brightness_scale", - "bri_stat_t": "brightness_state_topic", - "bri_tpl": "brightness_template", - "bri_val_tpl": "brightness_value_template", - "clr_temp_cmd_tpl": "color_temp_command_template", - "bat_lev_t": "battery_level_topic", - "bat_lev_tpl": "battery_level_template", - "chrg_t": "charging_topic", - "chrg_tpl": "charging_template", - "clr_temp_cmd_t": "color_temp_command_topic", - "clr_temp_stat_t": "color_temp_state_topic", - "clr_temp_val_tpl": "color_temp_value_template", - "cln_t": "cleaning_topic", - "cln_tpl": "cleaning_template", - "cmd_off_tpl": "command_off_template", - "cmd_on_tpl": "command_on_template", - "cmd_t": "command_topic", - "curr_temp_t": "current_temperature_topic", - "curr_temp_tpl": "current_temperature_template", - "dev": "device", - "dev_cla": "device_class", - "dock_t": "docked_topic", - "dock_tpl": "docked_template", - "err_t": "error_topic", - "err_tpl": "error_template", - "fanspd_t": "fan_speed_topic", - "fanspd_tpl": "fan_speed_template", - "fanspd_lst": "fan_speed_list", - "fx_cmd_t": "effect_command_topic", - "fx_list": "effect_list", - "fx_stat_t": "effect_state_topic", - "fx_tpl": "effect_template", - "fx_val_tpl": "effect_value_template", - "exp_aft": "expire_after", - "fan_mode_cmd_t": "fan_mode_command_topic", - "fan_mode_stat_tpl": "fan_mode_state_template", - "fan_mode_stat_t": "fan_mode_state_topic", - "frc_upd": "force_update", - "g_tpl": "green_template", - "hold_cmd_t": "hold_command_topic", - "hold_stat_tpl": "hold_state_template", - "hold_stat_t": "hold_state_topic", - "ic": "icon", - "init": "initial", - "json_attr": "json_attributes", - "json_attr_t": "json_attributes_topic", - "max_temp": "max_temp", - "min_temp": "min_temp", - "mode_cmd_t": "mode_command_topic", - "mode_stat_tpl": "mode_state_template", - "mode_stat_t": "mode_state_topic", - "name": "name", - "on_cmd_type": "on_command_type", - "opt": "optimistic", - "osc_cmd_t": "oscillation_command_topic", - "osc_stat_t": "oscillation_state_topic", - "osc_val_tpl": "oscillation_value_template", - "pl_arm_away": "payload_arm_away", - "pl_arm_home": "payload_arm_home", - "pl_avail": "payload_available", - "pl_cls": "payload_close", - "pl_disarm": "payload_disarm", - "pl_hi_spd": "payload_high_speed", - "pl_lock": "payload_lock", - "pl_lo_spd": "payload_low_speed", - "pl_med_spd": "payload_medium_speed", - "pl_not_avail": "payload_not_available", - "pl_off": "payload_off", - "pl_on": "payload_on", - "pl_open": "payload_open", - "pl_osc_off": "payload_oscillation_off", - "pl_osc_on": "payload_oscillation_on", - "pl_stop": "payload_stop", - "pl_unlk": "payload_unlock", - "pow_cmd_t": "power_command_topic", - "r_tpl": "red_template", - "ret": "retain", - "rgb_cmd_tpl": "rgb_command_template", - "rgb_cmd_t": "rgb_command_topic", - "rgb_stat_t": "rgb_state_topic", - "rgb_val_tpl": "rgb_value_template", - "send_cmd_t": "send_command_topic", - "send_if_off": "send_if_off", - "set_pos_tpl": "set_position_template", - "set_pos_t": "set_position_topic", - "pos_t": "position_topic", - "spd_cmd_t": "speed_command_topic", - "spd_stat_t": "speed_state_topic", - "spd_val_tpl": "speed_value_template", - "spds": "speeds", - "stat_clsd": "state_closed", - "stat_off": "state_off", - "stat_on": "state_on", - "stat_open": "state_open", - "stat_t": "state_topic", - "stat_tpl": "state_template", - "stat_val_tpl": "state_value_template", - "sup_feat": "supported_features", - "swing_mode_cmd_t": "swing_mode_command_topic", - "swing_mode_stat_tpl": "swing_mode_state_template", - "swing_mode_stat_t": "swing_mode_state_topic", - "temp_cmd_t": "temperature_command_topic", - "temp_stat_tpl": "temperature_state_template", - "temp_stat_t": "temperature_state_topic", - "tilt_clsd_val": "tilt_closed_value", - "tilt_cmd_t": "tilt_command_topic", - "tilt_inv_stat": "tilt_invert_state", - "tilt_max": "tilt_max", - "tilt_min": "tilt_min", - "tilt_opnd_val": "tilt_opened_value", - "tilt_status_opt": "tilt_status_optimistic", - "tilt_status_t": "tilt_status_topic", - "t": "topic", - "uniq_id": "unique_id", - "unit_of_meas": "unit_of_measurement", - "val_tpl": "value_template", - "whit_val_cmd_t": "white_value_command_topic", - "whit_val_scl": "white_value_scale", - "whit_val_stat_t": "white_value_state_topic", - "whit_val_tpl": "white_value_template", - "xy_cmd_t": "xy_command_topic", - "xy_stat_t": "xy_state_topic", - "xy_val_tpl": "xy_value_template", -} - -DEVICE_ABBREVIATIONS = { - "cns": "connections", - "ids": "identifiers", - "name": "name", - "mf": "manufacturer", - "mdl": "model", - "sw": "sw_version", -} - def clear_discovery_hash(hass, discovery_hash): """Clear entry in ALREADY_DISCOVERED list.""" diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index d88ab8c4f70..860ef52a98a 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -1,7 +1,14 @@ """The tests for the MQTT discovery.""" +from pathlib import Path +import re + from unittest.mock import patch from homeassistant.components import mqtt +from homeassistant.components.mqtt.abbreviations import ( + ABBREVIATIONS, + DEVICE_ABBREVIATIONS, +) from homeassistant.components.mqtt.discovery import ALREADY_DISCOVERED, async_start from homeassistant.const import STATE_OFF, STATE_ON @@ -245,6 +252,59 @@ async def test_discovery_expansion(hass, mqtt_mock, caplog): assert state.state == STATE_ON +ABBREVIATIONS_WHITE_LIST = [ + # MQTT client/server settings + "CONF_BIRTH_MESSAGE", + "CONF_BROKER", + "CONF_CERTIFICATE", + "CONF_CLIENT_CERT", + "CONF_CLIENT_ID", + "CONF_CLIENT_KEY", + "CONF_DISCOVERY", + "CONF_DISCOVERY_PREFIX", + "CONF_EMBEDDED", + "CONF_KEEPALIVE", + "CONF_TLS_INSECURE", + "CONF_TLS_VERSION", + "CONF_WILL_MESSAGE", + # Undocumented device configuration + "CONF_DEPRECATED_VIA_HUB", + "CONF_VIA_DEVICE", + # Already short + "CONF_FAN_MODE_LIST", + "CONF_HOLD_LIST", + "CONF_HS", + "CONF_MODE_LIST", + "CONF_PRECISION", + "CONF_QOS", + "CONF_SCHEMA", + "CONF_SWING_MODE_LIST", + "CONF_TEMP_STEP", +] + + +async def test_missing_discover_abbreviations(hass, mqtt_mock, caplog): + """Check MQTT platforms for missing abbreviations.""" + missing = [] + regex = re.compile(r"(CONF_[a-zA-Z\d_]*) *= *[\'\"]([a-zA-Z\d_]*)[\'\"]") + for fil in Path(mqtt.__file__).parent.rglob("*.py"): + with open(fil) as file: + matches = re.findall(regex, file.read()) + for match in matches: + if ( + match[1] not in ABBREVIATIONS.values() + and match[1] not in DEVICE_ABBREVIATIONS.values() + and match[0] not in ABBREVIATIONS_WHITE_LIST + ): + missing.append( + "{}: no abbreviation for {} ({})".format( + fil, match[1], match[0] + ) + ) + + assert not missing + + async def test_implicit_state_topic_alarm(hass, mqtt_mock, caplog): """Test implicit state topic for alarm_control_panel.""" entry = MockConfigEntry(domain=mqtt.DOMAIN)