diff --git a/homeassistant/components/ads/select.py b/homeassistant/components/ads/select.py index 5d887aa39c72..a359c25389db 100644 --- a/homeassistant/components/ads/select.py +++ b/homeassistant/components/ads/select.py @@ -19,6 +19,7 @@ from .hub import AdsHub DEFAULT_NAME = "ADS select" +# pylint: disable-next=home-assistant-duplicate-const CONF_OPTIONS = "options" PLATFORM_SCHEMA = SELECT_PLATFORM_SCHEMA.extend( diff --git a/homeassistant/components/altruist/const.py b/homeassistant/components/altruist/const.py index 93cbbd2c5357..03aad3c4d254 100644 --- a/homeassistant/components/altruist/const.py +++ b/homeassistant/components/altruist/const.py @@ -2,4 +2,5 @@ DOMAIN = "altruist" +# pylint: disable-next=home-assistant-duplicate-const CONF_HOST = "host" diff --git a/homeassistant/components/anthropic/const.py b/homeassistant/components/anthropic/const.py index 85f7ba0d88e1..64295d022f4a 100644 --- a/homeassistant/components/anthropic/const.py +++ b/homeassistant/components/anthropic/const.py @@ -10,6 +10,7 @@ DEFAULT_CONVERSATION_NAME = "Claude conversation" DEFAULT_AI_TASK_NAME = "Claude AI Task" CONF_RECOMMENDED = "recommended" +# pylint: disable-next=home-assistant-duplicate-const CONF_PROMPT = "prompt" CONF_CHAT_MODEL = "chat_model" CONF_CODE_EXECUTION = "code_execution" diff --git a/homeassistant/components/aurora_abb_powerone/const.py b/homeassistant/components/aurora_abb_powerone/const.py index 904f103d1c36..0520f72c3459 100644 --- a/homeassistant/components/aurora_abb_powerone/const.py +++ b/homeassistant/components/aurora_abb_powerone/const.py @@ -19,6 +19,8 @@ DEVICES = "devices" MANUFACTURER = "ABB" ATTR_DEVICE_NAME = "device_name" +# pylint: disable-next=home-assistant-duplicate-const ATTR_DEVICE_ID = "device_id" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL = "model" ATTR_FIRMWARE = "firmware" diff --git a/homeassistant/components/aws_s3/const.py b/homeassistant/components/aws_s3/const.py index b4eed69c4a96..4425b36dd8dd 100644 --- a/homeassistant/components/aws_s3/const.py +++ b/homeassistant/components/aws_s3/const.py @@ -11,6 +11,7 @@ CONF_ACCESS_KEY_ID = "access_key_id" CONF_SECRET_ACCESS_KEY = "secret_access_key" CONF_ENDPOINT_URL = "endpoint_url" CONF_BUCKET = "bucket" +# pylint: disable-next=home-assistant-duplicate-const CONF_PREFIX = "prefix" AWS_DOMAIN = "amazonaws.com" diff --git a/homeassistant/components/backblaze_b2/const.py b/homeassistant/components/backblaze_b2/const.py index 24c91ffcf668..eb7e8d9b6611 100644 --- a/homeassistant/components/backblaze_b2/const.py +++ b/homeassistant/components/backblaze_b2/const.py @@ -10,6 +10,7 @@ DOMAIN: Final = "backblaze_b2" CONF_KEY_ID = "key_id" CONF_APPLICATION_KEY = "application_key" CONF_BUCKET = "bucket" +# pylint: disable-next=home-assistant-duplicate-const CONF_PREFIX = "prefix" DATA_BACKUP_AGENT_LISTENERS: HassKey[list[Callable[[], None]]] = HassKey( diff --git a/homeassistant/components/bluetooth/const.py b/homeassistant/components/bluetooth/const.py index 904b066d95ee..c8f613ffb9c2 100644 --- a/homeassistant/components/bluetooth/const.py +++ b/homeassistant/components/bluetooth/const.py @@ -16,6 +16,7 @@ CONF_DETAILS = "details" CONF_PASSIVE = "passive" +# pylint: disable-next=home-assistant-duplicate-const CONF_SOURCE: Final = "source" CONF_SOURCE_DOMAIN: Final = "source_domain" CONF_SOURCE_MODEL: Final = "source_model" diff --git a/homeassistant/components/braviatv/const.py b/homeassistant/components/braviatv/const.py index 164b83cb5fe3..f63cc95bb67e 100644 --- a/homeassistant/components/braviatv/const.py +++ b/homeassistant/components/braviatv/const.py @@ -6,6 +6,7 @@ from typing import Final ATTR_CID: Final = "cid" ATTR_MAC: Final = "macAddr" ATTR_MANUFACTURER: Final = "Sony" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL: Final = "model" CONF_NICKNAME: Final = "nickname" diff --git a/homeassistant/components/bsblan/services.py b/homeassistant/components/bsblan/services.py index f111c84dce67..d9bd19e9f700 100644 --- a/homeassistant/components/bsblan/services.py +++ b/homeassistant/components/bsblan/services.py @@ -20,6 +20,7 @@ if TYPE_CHECKING: LOGGER = logging.getLogger(__name__) +# pylint: disable-next=home-assistant-duplicate-const ATTR_DEVICE_ID = "device_id" ATTR_MONDAY_SLOTS = "monday_slots" ATTR_TUESDAY_SLOTS = "tuesday_slots" diff --git a/homeassistant/components/calendar/const.py b/homeassistant/components/calendar/const.py index f568fb83d87e..7a3cba0a0b9a 100644 --- a/homeassistant/components/calendar/const.py +++ b/homeassistant/components/calendar/const.py @@ -13,6 +13,7 @@ if TYPE_CHECKING: DOMAIN = "calendar" DATA_COMPONENT: HassKey[EntityComponent[CalendarEntity]] = HassKey(DOMAIN) +# pylint: disable-next=home-assistant-duplicate-const CONF_EVENT = "event" diff --git a/homeassistant/components/citybikes/sensor.py b/homeassistant/components/citybikes/sensor.py index f1d30646dbe2..640553d2ba3c 100644 --- a/homeassistant/components/citybikes/sensor.py +++ b/homeassistant/components/citybikes/sensor.py @@ -45,7 +45,9 @@ HA_USER_AGENT = ( ) ATTR_UID = "uid" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LATITUDE = "latitude" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LONGITUDE = "longitude" ATTR_EMPTY_SLOTS = "empty_slots" ATTR_FREE_EBIKES = "free_ebikes" diff --git a/homeassistant/components/clicksend_tts/notify.py b/homeassistant/components/clicksend_tts/notify.py index 3baa4cbe379b..aaaa1394269a 100644 --- a/homeassistant/components/clicksend_tts/notify.py +++ b/homeassistant/components/clicksend_tts/notify.py @@ -29,6 +29,7 @@ BASE_API_URL = "https://rest.clicksend.com/v3" HEADERS = {"Content-Type": CONTENT_TYPE_JSON} +# pylint: disable-next=home-assistant-duplicate-const CONF_LANGUAGE = "language" CONF_VOICE = "voice" diff --git a/homeassistant/components/cloudflare_r2/const.py b/homeassistant/components/cloudflare_r2/const.py index 28b685e22e4c..d948da58e5f2 100644 --- a/homeassistant/components/cloudflare_r2/const.py +++ b/homeassistant/components/cloudflare_r2/const.py @@ -11,6 +11,7 @@ CONF_ACCESS_KEY_ID = "access_key_id" CONF_SECRET_ACCESS_KEY = "secret_access_key" CONF_ENDPOINT_URL = "endpoint_url" CONF_BUCKET = "bucket" +# pylint: disable-next=home-assistant-duplicate-const CONF_PREFIX = "prefix" # R2 is S3-compatible. Endpoint should be like: diff --git a/homeassistant/components/color_extractor/const.py b/homeassistant/components/color_extractor/const.py index 25c15ed9dc04..c94cb5bc35fe 100644 --- a/homeassistant/components/color_extractor/const.py +++ b/homeassistant/components/color_extractor/const.py @@ -6,4 +6,5 @@ ATTR_URL = "color_extract_url" DOMAIN = "color_extractor" DEFAULT_NAME = "Color extractor" +# pylint: disable-next=home-assistant-duplicate-const SERVICE_TURN_ON = "turn_on" diff --git a/homeassistant/components/conversation/const.py b/homeassistant/components/conversation/const.py index 20fee62c47b9..cd832f882802 100644 --- a/homeassistant/components/conversation/const.py +++ b/homeassistant/components/conversation/const.py @@ -19,6 +19,7 @@ ATTR_AGENT_ID = "agent_id" ATTR_CONVERSATION_ID = "conversation_id" SERVICE_PROCESS = "process" +# pylint: disable-next=home-assistant-duplicate-const SERVICE_RELOAD = "reload" DATA_COMPONENT: HassKey[EntityComponent[ConversationEntity]] = HassKey(DOMAIN) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index 6edc9beaf38f..57e0856a2972 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -43,6 +43,7 @@ PLATFORMS = [ ] ATTR_DARK = "dark" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LOCKED = "locked" ATTR_OFFSET = "offset" ATTR_ON = "on" diff --git a/homeassistant/components/drop_connect/const.py b/homeassistant/components/drop_connect/const.py index f1012f9652c1..2db1e301d5cb 100644 --- a/homeassistant/components/drop_connect/const.py +++ b/homeassistant/components/drop_connect/const.py @@ -4,6 +4,7 @@ CONF_COMMAND_TOPIC = "drop_command_topic" CONF_DATA_TOPIC = "drop_data_topic" CONF_DEVICE_DESC = "device_desc" +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE_ID = "device_id" CONF_DEVICE_TYPE = "device_type" CONF_HUB_ID = "drop_hub_id" diff --git a/homeassistant/components/drop_connect/sensor.py b/homeassistant/components/drop_connect/sensor.py index d21bc6932223..e963aaa04279 100644 --- a/homeassistant/components/drop_connect/sensor.py +++ b/homeassistant/components/drop_connect/sensor.py @@ -49,6 +49,7 @@ CURRENT_SYSTEM_PRESSURE = "current_system_pressure" HIGH_SYSTEM_PRESSURE = "high_system_pressure" LOW_SYSTEM_PRESSURE = "low_system_pressure" BATTERY = "battery" +# pylint: disable-next=home-assistant-duplicate-const TEMPERATURE = "temperature" INLET_TDS = "inlet_tds" OUTLET_TDS = "outlet_tds" diff --git a/homeassistant/components/elevenlabs/const.py b/homeassistant/components/elevenlabs/const.py index a13c3e6a3bf5..43b062a2a947 100644 --- a/homeassistant/components/elevenlabs/const.py +++ b/homeassistant/components/elevenlabs/const.py @@ -1,8 +1,10 @@ """Constants for the ElevenLabs text-to-speech integration.""" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL = "model" CONF_VOICE = "voice" +# pylint: disable-next=home-assistant-duplicate-const CONF_MODEL = "model" CONF_CONFIGURE_VOICE = "configure_voice" CONF_STABILITY = "stability" diff --git a/homeassistant/components/elkm1/alarm_control_panel.py b/homeassistant/components/elkm1/alarm_control_panel.py index 8ec116036bf5..f06735ded8c5 100644 --- a/homeassistant/components/elkm1/alarm_control_panel.py +++ b/homeassistant/components/elkm1/alarm_control_panel.py @@ -43,6 +43,7 @@ DISPLAY_MESSAGE_SERVICE_SCHEMA: VolDictType = { } SERVICE_ALARM_DISPLAY_MESSAGE = "alarm_display_message" +# pylint: disable-next=home-assistant-duplicate-const SERVICE_ALARM_ARM_VACATION = "alarm_arm_vacation" SERVICE_ALARM_ARM_HOME_INSTANT = "alarm_arm_home_instant" SERVICE_ALARM_ARM_NIGHT_INSTANT = "alarm_arm_night_instant" diff --git a/homeassistant/components/elkm1/config_flow.py b/homeassistant/components/elkm1/config_flow.py index fb134430632a..51071a6f9ff8 100644 --- a/homeassistant/components/elkm1/config_flow.py +++ b/homeassistant/components/elkm1/config_flow.py @@ -32,6 +32,7 @@ from .discovery import ( async_update_entry_from_discovery, ) +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE = "device" NON_SECURE_PORT = 2101 diff --git a/homeassistant/components/energyid/const.py b/homeassistant/components/energyid/const.py index 1fe5008fff82..e8b25e5ec1b9 100644 --- a/homeassistant/components/energyid/const.py +++ b/homeassistant/components/energyid/const.py @@ -8,6 +8,7 @@ NAME: Final = "EnergyID" # --- Config Flow and Entry Data --- CONF_PROVISIONING_KEY: Final = "provisioning_key" CONF_PROVISIONING_SECRET: Final = "provisioning_secret" +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE_ID: Final = "device_id" CONF_DEVICE_NAME: Final = "device_name" diff --git a/homeassistant/components/fish_audio/const.py b/homeassistant/components/fish_audio/const.py index 93f4c244d8ce..090f6864d9f0 100644 --- a/homeassistant/components/fish_audio/const.py +++ b/homeassistant/components/fish_audio/const.py @@ -5,12 +5,15 @@ from typing import Literal DOMAIN = "fish_audio" +# pylint: disable-next=home-assistant-duplicate-const CONF_NAME: Literal["name"] = "name" CONF_USER_ID: Literal["user_id"] = "user_id" +# pylint: disable-next=home-assistant-duplicate-const CONF_API_KEY: Literal["api_key"] = "api_key" CONF_VOICE_ID: Literal["voice_id"] = "voice_id" CONF_BACKEND: Literal["backend"] = "backend" CONF_SELF_ONLY: Literal["self_only"] = "self_only" +# pylint: disable-next=home-assistant-duplicate-const CONF_LANGUAGE: Literal["language"] = "language" CONF_SORT_BY: Literal["sort_by"] = "sort_by" CONF_LATENCY: Literal["latency"] = "latency" diff --git a/homeassistant/components/fitbit/const.py b/homeassistant/components/fitbit/const.py index bb503c7f348c..88b5c7cdf3af 100644 --- a/homeassistant/components/fitbit/const.py +++ b/homeassistant/components/fitbit/const.py @@ -13,6 +13,7 @@ ATTR_LAST_SAVED_AT: Final = "last_saved_at" ATTR_DURATION: Final = "duration" ATTR_DISTANCE: Final = "distance" +# pylint: disable-next=home-assistant-duplicate-const ATTR_ELEVATION: Final = "elevation" ATTR_HEIGHT: Final = "height" ATTR_WEIGHT: Final = "weight" diff --git a/homeassistant/components/fujitsu_fglair/const.py b/homeassistant/components/fujitsu_fglair/const.py index 73c811a1ed51..1596bc779097 100644 --- a/homeassistant/components/fujitsu_fglair/const.py +++ b/homeassistant/components/fujitsu_fglair/const.py @@ -7,6 +7,7 @@ API_REFRESH = timedelta(minutes=5) DOMAIN = "fujitsu_fglair" +# pylint: disable-next=home-assistant-duplicate-const CONF_REGION = "region" CONF_EUROPE = "is_europe" REGION_EU = "eu" diff --git a/homeassistant/components/generic_hygrostat/__init__.py b/homeassistant/components/generic_hygrostat/__init__.py index 5b3edcb976ef..5e5641868a9a 100644 --- a/homeassistant/components/generic_hygrostat/__init__.py +++ b/homeassistant/components/generic_hygrostat/__init__.py @@ -28,6 +28,7 @@ CONF_SENSOR = "target_sensor" CONF_MIN_HUMIDITY = "min_humidity" CONF_MAX_HUMIDITY = "max_humidity" CONF_TARGET_HUMIDITY = "target_humidity" +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE_CLASS = "device_class" CONF_MIN_DUR = "min_cycle_duration" CONF_DRY_TOLERANCE = "dry_tolerance" diff --git a/homeassistant/components/google_generative_ai_conversation/const.py b/homeassistant/components/google_generative_ai_conversation/const.py index f509c4a52fa6..2d77d2200199 100644 --- a/homeassistant/components/google_generative_ai_conversation/const.py +++ b/homeassistant/components/google_generative_ai_conversation/const.py @@ -15,6 +15,7 @@ DEFAULT_STT_NAME = "Google AI STT" DEFAULT_TTS_NAME = "Google AI TTS" DEFAULT_AI_TASK_NAME = "Google AI Task" +# pylint: disable-next=home-assistant-duplicate-const CONF_PROMPT = "prompt" DEFAULT_STT_PROMPT = "Transcribe the attached audio" diff --git a/homeassistant/components/google_travel_time/const.py b/homeassistant/components/google_travel_time/const.py index 6ae4dd419edb..abd910a1d043 100644 --- a/homeassistant/components/google_travel_time/const.py +++ b/homeassistant/components/google_travel_time/const.py @@ -12,6 +12,7 @@ DOMAIN = "google_travel_time" ATTRIBUTION = "Powered by Google" CONF_DESTINATION = "destination" +# pylint: disable-next=home-assistant-duplicate-const CONF_OPTIONS = "options" CONF_ORIGIN = "origin" CONF_AVOID = "avoid" diff --git a/homeassistant/components/gpsd/sensor.py b/homeassistant/components/gpsd/sensor.py index 0efb15aee92f..b30d3a93f0b4 100644 --- a/homeassistant/components/gpsd/sensor.py +++ b/homeassistant/components/gpsd/sensor.py @@ -34,6 +34,7 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) ATTR_CLIMB = "climb" +# pylint: disable-next=home-assistant-duplicate-const ATTR_ELEVATION = "elevation" ATTR_SPEED = "speed" ATTR_TOTAL_SATELLITES = "total_satellites" diff --git a/homeassistant/components/growatt_server/const.py b/homeassistant/components/growatt_server/const.py index 555a5e305479..92445b612982 100644 --- a/homeassistant/components/growatt_server/const.py +++ b/homeassistant/components/growatt_server/const.py @@ -3,10 +3,12 @@ from homeassistant.const import Platform CONF_PLANT_ID = "plant_id" +# pylint: disable-next=home-assistant-duplicate-const CONF_REGION = "region" # API key support +# pylint: disable-next=home-assistant-duplicate-const CONF_API_KEY = "api_key" # Auth types for config flow diff --git a/homeassistant/components/hassio/const.py b/homeassistant/components/hassio/const.py index 42c6415fc28d..48879c7b7b61 100644 --- a/homeassistant/components/hassio/const.py +++ b/homeassistant/components/hassio/const.py @@ -131,9 +131,11 @@ ATTR_AUTO_UPDATE = "auto_update" ATTR_VERSION = "version" ATTR_VERSION_LATEST = "version_latest" ATTR_CPU_PERCENT = "cpu_percent" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LOCATION = "location" ATTR_MEMORY_PERCENT = "memory_percent" ATTR_SLUG = "slug" +# pylint: disable-next=home-assistant-duplicate-const ATTR_STATE = "state" ATTR_STARTED = "started" ATTR_URL = "url" diff --git a/homeassistant/components/hddtemp/sensor.py b/homeassistant/components/hddtemp/sensor.py index 3e168ed52b5d..251d4f0f5abe 100644 --- a/homeassistant/components/hddtemp/sensor.py +++ b/homeassistant/components/hddtemp/sensor.py @@ -28,6 +28,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType _LOGGER = logging.getLogger(__name__) ATTR_DEVICE = "device" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL = "model" DEFAULT_HOST = "localhost" diff --git a/homeassistant/components/hegel/const.py b/homeassistant/components/hegel/const.py index 692ac267e5c1..587f380f4bf4 100644 --- a/homeassistant/components/hegel/const.py +++ b/homeassistant/components/hegel/const.py @@ -3,6 +3,7 @@ DOMAIN = "hegel" DEFAULT_PORT = 50001 +# pylint: disable-next=home-assistant-duplicate-const CONF_MODEL = "model" CONF_MAX_VOLUME = "max_volume" # 1.0 means amp's internal max diff --git a/homeassistant/components/hive/const.py b/homeassistant/components/hive/const.py index 1b679086563a..3f43f29595c0 100644 --- a/homeassistant/components/hive/const.py +++ b/homeassistant/components/hive/const.py @@ -2,6 +2,7 @@ from homeassistant.const import Platform +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODE = "mode" ATTR_TIME_PERIOD = "time_period" ATTR_ONOFF = "on_off" diff --git a/homeassistant/components/homeassistant/triggers/state.py b/homeassistant/components/homeassistant/triggers/state.py index 431d080ca097..55265284612c 100644 --- a/homeassistant/components/homeassistant/triggers/state.py +++ b/homeassistant/components/homeassistant/triggers/state.py @@ -32,6 +32,7 @@ from homeassistant.helpers.typing import ConfigType _LOGGER = logging.getLogger(__name__) +# pylint: disable-next=home-assistant-duplicate-const CONF_ENTITY_ID = "entity_id" CONF_FROM = "from" CONF_TO = "to" diff --git a/homeassistant/components/homeassistant/triggers/time.py b/homeassistant/components/homeassistant/triggers/time.py index 398a399f4c37..2cf5d8cf109f 100644 --- a/homeassistant/components/homeassistant/triggers/time.py +++ b/homeassistant/components/homeassistant/triggers/time.py @@ -38,6 +38,7 @@ from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo from homeassistant.helpers.typing import ConfigType from homeassistant.util import dt as dt_util +# pylint: disable-next=home-assistant-duplicate-const CONF_WEEKDAY = "weekday" _TIME_TRIGGER_ENTITY = vol.All(str, cv.entity_domain(["input_datetime", "sensor"])) diff --git a/homeassistant/components/homeworks/__init__.py b/homeassistant/components/homeworks/__init__.py index f9531e759a78..435afe50a0a1 100644 --- a/homeassistant/components/homeworks/__init__.py +++ b/homeassistant/components/homeworks/__init__.py @@ -39,6 +39,7 @@ _LOGGER = logging.getLogger(__name__) PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.LIGHT] +# pylint: disable-next=home-assistant-duplicate-const CONF_COMMAND = "command" EVENT_BUTTON_PRESS = "homeworks_button_press" diff --git a/homeassistant/components/html5/const.py b/homeassistant/components/html5/const.py index cc0aeb282c7c..8f88a4e28a5c 100644 --- a/homeassistant/components/html5/const.py +++ b/homeassistant/components/html5/const.py @@ -15,6 +15,7 @@ ATTR_ACTIONS = "actions" ATTR_BADGE = "badge" ATTR_DATA = "data" ATTR_DIR = "dir" +# pylint: disable-next=home-assistant-duplicate-const ATTR_ICON = "icon" ATTR_IMAGE = "image" ATTR_LANG = "lang" diff --git a/homeassistant/components/humidifier/condition.py b/homeassistant/components/humidifier/condition.py index 406bdd88b8c7..3df1b7137e60 100644 --- a/homeassistant/components/humidifier/condition.py +++ b/homeassistant/components/humidifier/condition.py @@ -27,6 +27,7 @@ from .const import ( HumidifierEntityFeature, ) +# pylint: disable-next=home-assistant-duplicate-const CONF_MODE = "mode" IS_MODE_CONDITION_SCHEMA = ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL.extend( diff --git a/homeassistant/components/humidifier/trigger.py b/homeassistant/components/humidifier/trigger.py index b0df91267337..2914536d0ef3 100644 --- a/homeassistant/components/humidifier/trigger.py +++ b/homeassistant/components/humidifier/trigger.py @@ -18,6 +18,7 @@ from homeassistant.helpers.trigger import ( from .const import ATTR_ACTION, DOMAIN, HumidifierAction, HumidifierEntityFeature +# pylint: disable-next=home-assistant-duplicate-const CONF_MODE = "mode" MODE_CHANGED_TRIGGER_SCHEMA = ENTITY_STATE_TRIGGER_SCHEMA_FIRST_LAST.extend( diff --git a/homeassistant/components/husqvarna_automower/event.py b/homeassistant/components/husqvarna_automower/event.py index 8a907323e116..2e4b79e1da5a 100644 --- a/homeassistant/components/husqvarna_automower/event.py +++ b/homeassistant/components/husqvarna_automower/event.py @@ -23,7 +23,9 @@ _LOGGER = logging.getLogger(__name__) PARALLEL_UPDATES = 1 ATTR_SEVERITY = "severity" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LATITUDE = "latitude" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LONGITUDE = "longitude" ATTR_DATE_TIME = "date_time" diff --git a/homeassistant/components/input_select/__init__.py b/homeassistant/components/input_select/__init__.py index 405790e96c62..74bf041b95eb 100644 --- a/homeassistant/components/input_select/__init__.py +++ b/homeassistant/components/input_select/__init__.py @@ -37,6 +37,7 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = "input_select" CONF_INITIAL = "initial" +# pylint: disable-next=home-assistant-duplicate-const CONF_OPTIONS = "options" SERVICE_SET_OPTIONS = "set_options" diff --git a/homeassistant/components/intent_script/__init__.py b/homeassistant/components/intent_script/__init__.py index f866a5004603..5d9e42b34a38 100644 --- a/homeassistant/components/intent_script/__init__.py +++ b/homeassistant/components/intent_script/__init__.py @@ -29,6 +29,7 @@ CONF_INTENTS = "intents" CONF_SPEECH = "speech" CONF_REPROMPT = "reprompt" +# pylint: disable-next=home-assistant-duplicate-const CONF_ACTION = "action" CONF_CARD = "card" CONF_TITLE = "title" diff --git a/homeassistant/components/ios/const.py b/homeassistant/components/ios/const.py index c9782aab1c77..e8d32c6dcecf 100644 --- a/homeassistant/components/ios/const.py +++ b/homeassistant/components/ios/const.py @@ -32,6 +32,7 @@ CONF_ACTION_LABEL_TEXT = "text" CONF_ACTION_ICON = "icon" CONF_ACTION_ICON_COLOR = "color" CONF_ACTION_ICON_ICON = "icon" +# pylint: disable-next=home-assistant-duplicate-const CONF_ACTIONS = "actions" CONF_ACTION_SHOW_IN_CARPLAY = "show_in_carplay" CONF_ACTION_SHOW_IN_WATCH = "show_in_watch" diff --git a/homeassistant/components/iperf3/__init__.py b/homeassistant/components/iperf3/__init__.py index feca7e33f548..2e9d4785129f 100644 --- a/homeassistant/components/iperf3/__init__.py +++ b/homeassistant/components/iperf3/__init__.py @@ -34,6 +34,7 @@ DATA_UPDATED = f"{DOMAIN}_data_updated" _LOGGER = logging.getLogger(__name__) CONF_DURATION = "duration" +# pylint: disable-next=home-assistant-duplicate-const CONF_PARALLEL = "parallel" CONF_MANUAL = "manual" diff --git a/homeassistant/components/isy994/services.py b/homeassistant/components/isy994/services.py index d66060937b73..321caffaffae 100644 --- a/homeassistant/components/isy994/services.py +++ b/homeassistant/components/isy994/services.py @@ -38,6 +38,7 @@ SERVICE_DELETE_ZWAVE_LOCK_USER_CODE = "delete_zwave_lock_user_code" CONF_PARAMETER = "parameter" CONF_PARAMETERS = "parameters" CONF_USER_NUM = "user_num" +# pylint: disable-next=home-assistant-duplicate-const CONF_CODE = "code" CONF_VALUE = "value" CONF_INIT = "init" diff --git a/homeassistant/components/jewish_calendar/const.py b/homeassistant/components/jewish_calendar/const.py index b3a0dea5da0a..7146a3fad0c6 100644 --- a/homeassistant/components/jewish_calendar/const.py +++ b/homeassistant/components/jewish_calendar/const.py @@ -3,6 +3,7 @@ DOMAIN = "jewish_calendar" ATTR_AFTER_SUNSET = "after_sunset" +# pylint: disable-next=home-assistant-duplicate-const ATTR_DATE = "date" ATTR_NUSACH = "nusach" diff --git a/homeassistant/components/kiosker/const.py b/homeassistant/components/kiosker/const.py index d358be495e71..25232e1848f6 100644 --- a/homeassistant/components/kiosker/const.py +++ b/homeassistant/components/kiosker/const.py @@ -3,6 +3,7 @@ DOMAIN = "kiosker" # Configuration keys +# pylint: disable-next=home-assistant-duplicate-const CONF_API_TOKEN = "api_token" # Default values diff --git a/homeassistant/components/linux_battery/sensor.py b/homeassistant/components/linux_battery/sensor.py index b974aac7b363..59fe82e5265f 100644 --- a/homeassistant/components/linux_battery/sensor.py +++ b/homeassistant/components/linux_battery/sensor.py @@ -28,6 +28,7 @@ ATTR_CYCLE_COUNT = "cycle_count" ATTR_ENERGY_FULL = "energy_full" ATTR_ENERGY_FULL_DESIGN = "energy_full_design" ATTR_ENERGY_NOW = "energy_now" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MANUFACTURER = "manufacturer" ATTR_MODEL_NAME = "model_name" ATTR_POWER_NOW = "power_now" diff --git a/homeassistant/components/marantz_infrared/const.py b/homeassistant/components/marantz_infrared/const.py index d45e7c14215e..e89968c51bed 100644 --- a/homeassistant/components/marantz_infrared/const.py +++ b/homeassistant/components/marantz_infrared/const.py @@ -6,6 +6,7 @@ from homeassistant.util import slugify DOMAIN = "marantz_infrared" CONF_INFRARED_EMITTER_ENTITY_ID = "infrared_emitter_entity_id" +# pylint: disable-next=home-assistant-duplicate-const CONF_MODEL = "model" MODELS: dict[str, marantz_models.MarantzModel] = { diff --git a/homeassistant/components/mastodon/const.py b/homeassistant/components/mastodon/const.py index 805c01ad7627..85da7fb7d9c3 100644 --- a/homeassistant/components/mastodon/const.py +++ b/homeassistant/components/mastodon/const.py @@ -31,6 +31,7 @@ ATTR_AVATAR = "avatar" ATTR_AVATAR_MIME_TYPE = "avatar_mime_type" ATTR_HEADER = "header" ATTR_HEADER_MIME_TYPE = "header_mime_type" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LOCKED = "locked" ATTR_BOT = "bot" ATTR_DISCOVERABLE = "discoverable" diff --git a/homeassistant/components/mcp/const.py b/homeassistant/components/mcp/const.py index 19fad8f47369..e33e32e6d828 100644 --- a/homeassistant/components/mcp/const.py +++ b/homeassistant/components/mcp/const.py @@ -2,6 +2,7 @@ DOMAIN = "mcp" +# pylint: disable-next=home-assistant-duplicate-const CONF_ACCESS_TOKEN = "access_token" CONF_AUTHORIZATION_URL = "authorization_url" CONF_TOKEN_URL = "token_url" diff --git a/homeassistant/components/mcp_server/http.py b/homeassistant/components/mcp_server/http.py index 980015bc3a44..205684d00088 100644 --- a/homeassistant/components/mcp_server/http.py +++ b/homeassistant/components/mcp_server/http.py @@ -57,6 +57,7 @@ STREAMABLE_API = "/api/mcp" TIMEOUT = 60 # Seconds # Content types +# pylint: disable-next=home-assistant-duplicate-const CONTENT_TYPE_JSON = "application/json" # Legacy SSE endpoint diff --git a/homeassistant/components/meteoalarm/binary_sensor.py b/homeassistant/components/meteoalarm/binary_sensor.py index b96da4ead1ee..4dd40c88b4ce 100644 --- a/homeassistant/components/meteoalarm/binary_sensor.py +++ b/homeassistant/components/meteoalarm/binary_sensor.py @@ -22,7 +22,9 @@ _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Information provided by MeteoAlarm" +# pylint: disable-next=home-assistant-duplicate-const CONF_COUNTRY = "country" +# pylint: disable-next=home-assistant-duplicate-const CONF_LANGUAGE = "language" CONF_PROVINCE = "province" diff --git a/homeassistant/components/mikrotik/const.py b/homeassistant/components/mikrotik/const.py index 772c956b54b9..a13cd9804bd7 100644 --- a/homeassistant/components/mikrotik/const.py +++ b/homeassistant/components/mikrotik/const.py @@ -10,6 +10,7 @@ DEFAULT_DETECTION_TIME: Final = 300 ATTR_MANUFACTURER: Final = "Mikrotik" ATTR_SERIAL_NUMBER: Final = "serial-number" ATTR_FIRMWARE: Final = "current-firmware" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL: Final = "model" CONF_ARP_PING: Final = "arp_ping" diff --git a/homeassistant/components/minio/__init__.py b/homeassistant/components/minio/__init__.py index 30f66c88dad2..918e42825092 100644 --- a/homeassistant/components/minio/__init__.py +++ b/homeassistant/components/minio/__init__.py @@ -17,7 +17,9 @@ from .minio_helper import MinioEventThread, create_minio_client _LOGGER = logging.getLogger(__name__) DOMAIN = "minio" +# pylint: disable-next=home-assistant-duplicate-const CONF_HOST = "host" +# pylint: disable-next=home-assistant-duplicate-const CONF_PORT = "port" CONF_ACCESS_KEY = "access_key" CONF_SECRET_KEY = "secret_key" diff --git a/homeassistant/components/mobile_app/const.py b/homeassistant/components/mobile_app/const.py index cd88ac6131f1..455642546955 100644 --- a/homeassistant/components/mobile_app/const.py +++ b/homeassistant/components/mobile_app/const.py @@ -27,7 +27,9 @@ ATTR_APP_ID = "app_id" ATTR_APP_NAME = "app_name" ATTR_APP_VERSION = "app_version" ATTR_DEVICE_NAME = "device_name" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MANUFACTURER = "manufacturer" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL = "model" ATTR_NO_LEGACY_ENCRYPTION = "no_legacy_encryption" ATTR_OS_NAME = "os_name" diff --git a/homeassistant/components/motioneye/const.py b/homeassistant/components/motioneye/const.py index 9350747ca5fe..91c26699e409 100644 --- a/homeassistant/components/motioneye/const.py +++ b/homeassistant/components/motioneye/const.py @@ -29,6 +29,7 @@ DOMAIN: Final = "motioneye" ATTR_EVENT_TYPE: Final = "event_type" ATTR_WEBHOOK_ID: Final = "webhook_id" +# pylint: disable-next=home-assistant-duplicate-const CONF_ACTION: Final = "action" CONF_ADMIN_PASSWORD: Final = "admin_password" CONF_ADMIN_USERNAME: Final = "admin_username" diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 59878ac4a9a8..98cb304392b6 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -15,6 +15,7 @@ ATTR_MESSAGE_EXPIRY_INTERVAL = "message_expiry_interval" ATTR_PAYLOAD = "payload" ATTR_QOS = "qos" ATTR_RETAIN = "retain" +# pylint: disable-next=home-assistant-duplicate-const ATTR_SERIAL_NUMBER = "serial_number" ATTR_TOPIC = "topic" @@ -50,6 +51,7 @@ CONF_IMAGE_TOPIC = "image_topic" CONF_JSON_ATTRS_TOPIC = "json_attributes_topic" CONF_JSON_ATTRS_TEMPLATE = "json_attributes_template" CONF_KEEPALIVE = "keepalive" +# pylint: disable-next=home-assistant-duplicate-const CONF_OPTIONS = "options" CONF_ORIGIN = "origin" CONF_QOS = ATTR_QOS diff --git a/homeassistant/components/mqtt/humidifier.py b/homeassistant/components/mqtt/humidifier.py index 5e6c4a548142..003aef0f1c42 100644 --- a/homeassistant/components/mqtt/humidifier.py +++ b/homeassistant/components/mqtt/humidifier.py @@ -60,6 +60,7 @@ from .util import valid_publish_topic, valid_subscribe_topic PARALLEL_UPDATES = 0 CONF_AVAILABLE_MODES_LIST = "modes" +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE_CLASS = "device_class" CONF_MODE_COMMAND_TEMPLATE = "mode_command_template" CONF_MODE_COMMAND_TOPIC = "mode_command_topic" diff --git a/homeassistant/components/mqtt/vacuum.py b/homeassistant/components/mqtt/vacuum.py index cc5cd38e71a0..f24150a647ab 100644 --- a/homeassistant/components/mqtt/vacuum.py +++ b/homeassistant/components/mqtt/vacuum.py @@ -36,9 +36,11 @@ FAN_SPEED = "fan_speed" SEGMENTS = "segments" STATE = "state" +# pylint: disable-next=home-assistant-duplicate-const STATE_IDLE = "idle" STATE_DOCKED = "docked" STATE_ERROR = "error" +# pylint: disable-next=home-assistant-duplicate-const STATE_PAUSED = "paused" STATE_RETURNING = "returning" STATE_CLEANING = "cleaning" diff --git a/homeassistant/components/music_assistant/const.py b/homeassistant/components/music_assistant/const.py index 035da439db61..93f33aa9f793 100644 --- a/homeassistant/components/music_assistant/const.py +++ b/homeassistant/components/music_assistant/const.py @@ -12,6 +12,7 @@ AUTH_SCHEMA_VERSION = 28 # Schema version where hassio discovery support was added HASSIO_DISCOVERY_SCHEMA_VERSION = 28 +# pylint: disable-next=home-assistant-duplicate-const CONF_TOKEN = "token" ATTR_IS_GROUP = "is_group" diff --git a/homeassistant/components/netatmo/const.py b/homeassistant/components/netatmo/const.py index b10d2309770d..244d0bccba06 100644 --- a/homeassistant/components/netatmo/const.py +++ b/homeassistant/components/netatmo/const.py @@ -92,6 +92,7 @@ ATTR_HOME_ID = "home_id" ATTR_HOME_NAME = "home_name" ATTR_IS_KNOWN = "is_known" ATTR_PERSON = "person" +# pylint: disable-next=home-assistant-duplicate-const ATTR_PERSONS = "persons" ATTR_PSEUDO = "pseudo" ATTR_SCHEDULE_ID = "schedule_id" diff --git a/homeassistant/components/nfandroidtv/const.py b/homeassistant/components/nfandroidtv/const.py index cd4b99d09818..b9b660ad13b4 100644 --- a/homeassistant/components/nfandroidtv/const.py +++ b/homeassistant/components/nfandroidtv/const.py @@ -27,6 +27,7 @@ ATTR_IMAGE_PATH = "path" ATTR_IMAGE_USERNAME = "username" ATTR_IMAGE_PASSWORD = "password" ATTR_IMAGE_AUTH = "auth" +# pylint: disable-next=home-assistant-duplicate-const ATTR_ICON = "icon" # Attributes contained in icon ATTR_ICON_URL = "url" diff --git a/homeassistant/components/nice_go/const.py b/homeassistant/components/nice_go/const.py index c02bcb3c2349..4713be5dc717 100644 --- a/homeassistant/components/nice_go/const.py +++ b/homeassistant/components/nice_go/const.py @@ -8,6 +8,7 @@ DOMAIN = "nice_go" # Configuration CONF_SITE_ID = "site_id" +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE_ID = "device_id" CONF_REFRESH_TOKEN = "refresh_token" CONF_REFRESH_TOKEN_CREATION_TIME = "refresh_token_creation_time" diff --git a/homeassistant/components/nina/const.py b/homeassistant/components/nina/const.py index 6953a63e9a0b..2099ead335e5 100644 --- a/homeassistant/components/nina/const.py +++ b/homeassistant/components/nina/const.py @@ -29,6 +29,7 @@ ATTR_SEVERITY: str = "severity" ATTR_RECOMMENDED_ACTIONS: str = "recommended_actions" ATTR_AFFECTED_AREAS: str = "affected_areas" ATTR_WEB: str = "web" +# pylint: disable-next=home-assistant-duplicate-const ATTR_ID: str = "id" ATTR_SENT: str = "sent" ATTR_START: str = "start" diff --git a/homeassistant/components/nmbs/const.py b/homeassistant/components/nmbs/const.py index 04c8beb327d7..7c458787d685 100644 --- a/homeassistant/components/nmbs/const.py +++ b/homeassistant/components/nmbs/const.py @@ -13,6 +13,7 @@ CONF_STATION_FROM = "station_from" CONF_STATION_TO = "station_to" CONF_STATION_LIVE = "station_live" CONF_EXCLUDE_VIAS = "exclude_vias" +# pylint: disable-next=home-assistant-duplicate-const CONF_SHOW_ON_MAP = "show_on_map" diff --git a/homeassistant/components/novy_cooker_hood/const.py b/homeassistant/components/novy_cooker_hood/const.py index e46aa3f3e1d2..1169ccc0423d 100644 --- a/homeassistant/components/novy_cooker_hood/const.py +++ b/homeassistant/components/novy_cooker_hood/const.py @@ -7,6 +7,7 @@ from rf_protocols import ModulationType DOMAIN: Final = "novy_cooker_hood" CONF_TRANSMITTER: Final = "transmitter" +# pylint: disable-next=home-assistant-duplicate-const CONF_CODE: Final = "code" CODE_MIN: Final = 1 diff --git a/homeassistant/components/ntfy/services.py b/homeassistant/components/ntfy/services.py index 45d87e5b9bb3..46e5a065379f 100644 --- a/homeassistant/components/ntfy/services.py +++ b/homeassistant/components/ntfy/services.py @@ -26,6 +26,7 @@ ATTR_CALL = "call" ATTR_CLICK = "click" ATTR_DELAY = "delay" ATTR_EMAIL = "email" +# pylint: disable-next=home-assistant-duplicate-const ATTR_ICON = "icon" ATTR_MARKDOWN = "markdown" ATTR_PRIORITY = "priority" diff --git a/homeassistant/components/numato/__init__.py b/homeassistant/components/numato/__init__.py index d3882bea290c..651577f6e168 100644 --- a/homeassistant/components/numato/__init__.py +++ b/homeassistant/components/numato/__init__.py @@ -28,6 +28,7 @@ DOMAIN = "numato" CONF_INVERT_LOGIC = "invert_logic" CONF_DISCOVER = "discover" +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICES = "devices" CONF_DEVICE_ID = "id" CONF_PORTS = "ports" diff --git a/homeassistant/components/ollama/const.py b/homeassistant/components/ollama/const.py index ac645145c023..0b540c0b3734 100644 --- a/homeassistant/components/ollama/const.py +++ b/homeassistant/components/ollama/const.py @@ -4,7 +4,9 @@ DOMAIN = "ollama" DEFAULT_NAME = "Ollama" +# pylint: disable-next=home-assistant-duplicate-const CONF_MODEL = "model" +# pylint: disable-next=home-assistant-duplicate-const CONF_PROMPT = "prompt" CONF_THINK = "think" diff --git a/homeassistant/components/onkyo/config_flow.py b/homeassistant/components/onkyo/config_flow.py index f4a85f56acb0..2561ada34e50 100644 --- a/homeassistant/components/onkyo/config_flow.py +++ b/homeassistant/components/onkyo/config_flow.py @@ -47,6 +47,7 @@ from .util import get_meaning _LOGGER = logging.getLogger(__name__) +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE = "device" INPUT_SOURCES_DEFAULT: list[InputSource] = [] diff --git a/homeassistant/components/openai_conversation/const.py b/homeassistant/components/openai_conversation/const.py index 11fd3b69e140..03a8a9be3c39 100644 --- a/homeassistant/components/openai_conversation/const.py +++ b/homeassistant/components/openai_conversation/const.py @@ -20,6 +20,7 @@ CONF_IMAGE_MODEL = "image_model" CONF_CODE_INTERPRETER = "code_interpreter" CONF_FILENAMES = "filenames" CONF_MAX_TOKENS = "max_tokens" +# pylint: disable-next=home-assistant-duplicate-const CONF_PROMPT = "prompt" CONF_REASONING_EFFORT = "reasoning_effort" CONF_REASONING_SUMMARY = "reasoning_summary" diff --git a/homeassistant/components/openerz/sensor.py b/homeassistant/components/openerz/sensor.py index d78d0feea221..5d05a44a6871 100644 --- a/homeassistant/components/openerz/sensor.py +++ b/homeassistant/components/openerz/sensor.py @@ -18,6 +18,7 @@ SCAN_INTERVAL = timedelta(hours=12) CONF_ZIP = "zip" CONF_WASTE_TYPE = "waste_type" +# pylint: disable-next=home-assistant-duplicate-const CONF_NAME = "name" PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend( diff --git a/homeassistant/components/openevse/const.py b/homeassistant/components/openevse/const.py index e15becdcec30..392a2eb50640 100644 --- a/homeassistant/components/openevse/const.py +++ b/homeassistant/components/openevse/const.py @@ -1,5 +1,6 @@ """Constants for the OpenEVSE integration.""" +# pylint: disable-next=home-assistant-duplicate-const CONF_ID = "id" CONF_SERIAL = "serial" DOMAIN = "openevse" diff --git a/homeassistant/components/osoenergy/water_heater.py b/homeassistant/components/osoenergy/water_heater.py index 2c76d7e3b052..fb71172765fa 100644 --- a/homeassistant/components/osoenergy/water_heater.py +++ b/homeassistant/components/osoenergy/water_heater.py @@ -45,7 +45,9 @@ SERVICE_GET_PROFILE = "get_profile" SERVICE_SET_PROFILE = "set_profile" SERVICE_SET_V40MIN = "set_v40_min" SERVICE_TURN_AWAY_MODE_ON = "turn_away_mode_on" +# pylint: disable-next=home-assistant-duplicate-const SERVICE_TURN_OFF = "turn_off" +# pylint: disable-next=home-assistant-duplicate-const SERVICE_TURN_ON = "turn_on" diff --git a/homeassistant/components/panasonic_viera/const.py b/homeassistant/components/panasonic_viera/const.py index f76c01e396bc..1c87f2227d37 100644 --- a/homeassistant/components/panasonic_viera/const.py +++ b/homeassistant/components/panasonic_viera/const.py @@ -15,6 +15,7 @@ ATTR_REMOTE = "remote" ATTR_DEVICE_INFO = "device_info" ATTR_FRIENDLY_NAME = "friendlyName" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MANUFACTURER = "manufacturer" ATTR_MODEL_NUMBER = "modelNumber" ATTR_UDN = "UDN" diff --git a/homeassistant/components/profiler/__init__.py b/homeassistant/components/profiler/__init__.py index 18e0e73c4d63..295b8cc6a83e 100644 --- a/homeassistant/components/profiler/__init__.py +++ b/homeassistant/components/profiler/__init__.py @@ -70,6 +70,7 @@ DEFAULT_SCAN_INTERVAL = timedelta(seconds=30) DEFAULT_MAX_OBJECTS = 5 +# pylint: disable-next=home-assistant-duplicate-const CONF_ENABLED = "enabled" CONF_SECONDS = "seconds" CONF_MAX_OBJECTS = "max_objects" diff --git a/homeassistant/components/proxmoxve/const.py b/homeassistant/components/proxmoxve/const.py index cd7bd7db54b6..cad6eac21274 100644 --- a/homeassistant/components/proxmoxve/const.py +++ b/homeassistant/components/proxmoxve/const.py @@ -7,6 +7,7 @@ CONF_AUTH_METHOD = "auth_method" CONF_REALM = "realm" CONF_NODE = "node" CONF_NODES = "nodes" +# pylint: disable-next=home-assistant-duplicate-const CONF_TOKEN = "token" CONF_TOKEN_ID = "token_id" CONF_TOKEN_SECRET = "token_value" diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index e0c6a3414ed0..db6bfdb1dcf4 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -67,6 +67,7 @@ PLATFORMS = [ Platform.UPDATE, ] +# pylint: disable-next=home-assistant-duplicate-const CONF_CONDITION = "condition" CONF_DEWPOINT = "dewpoint" CONF_ET = "et" diff --git a/homeassistant/components/renault/services.py b/homeassistant/components/renault/services.py index 971381330059..f9d4d8b6e416 100644 --- a/homeassistant/components/renault/services.py +++ b/homeassistant/components/renault/services.py @@ -19,6 +19,7 @@ if TYPE_CHECKING: LOGGER = logging.getLogger(__name__) ATTR_SCHEDULES = "schedules" +# pylint: disable-next=home-assistant-duplicate-const ATTR_TEMPERATURE = "temperature" ATTR_VEHICLE = "vehicle" ATTR_WHEN = "when" diff --git a/homeassistant/components/roborock/const.py b/homeassistant/components/roborock/const.py index 1ed0df695b86..9ba609887cf2 100644 --- a/homeassistant/components/roborock/const.py +++ b/homeassistant/components/roborock/const.py @@ -13,6 +13,7 @@ CONF_USER_DATA = "user_data" CONF_SHOW_BACKGROUND = "show_background" CONF_SHOW_WALLS = "show_walls" CONF_SHOW_ROOMS = "show_rooms" +# pylint: disable-next=home-assistant-duplicate-const CONF_REGION = "region" REGION_OPTIONS = ["auto", "us", "eu", "ru", "cn"] diff --git a/homeassistant/components/select/const.py b/homeassistant/components/select/const.py index 1e2aa488e8bf..38a1b6169b85 100644 --- a/homeassistant/components/select/const.py +++ b/homeassistant/components/select/const.py @@ -4,6 +4,7 @@ DOMAIN = "select" ATTR_CYCLE = "cycle" ATTR_OPTIONS = "options" +# pylint: disable-next=home-assistant-duplicate-const ATTR_OPTION = "option" CONF_CYCLE = "cycle" @@ -12,5 +13,6 @@ CONF_OPTION = "option" SERVICE_SELECT_FIRST = "select_first" SERVICE_SELECT_LAST = "select_last" SERVICE_SELECT_NEXT = "select_next" +# pylint: disable-next=home-assistant-duplicate-const SERVICE_SELECT_OPTION = "select_option" SERVICE_SELECT_PREVIOUS = "select_previous" diff --git a/homeassistant/components/sftp_storage/const.py b/homeassistant/components/sftp_storage/const.py index c4cac7d19b65..9a7a57a6dd1e 100644 --- a/homeassistant/components/sftp_storage/const.py +++ b/homeassistant/components/sftp_storage/const.py @@ -10,9 +10,13 @@ DOMAIN: Final = "sftp_storage" LOGGER = logging.getLogger(__package__) +# pylint: disable-next=home-assistant-duplicate-const CONF_HOST: Final = "host" +# pylint: disable-next=home-assistant-duplicate-const CONF_PORT: Final = "port" +# pylint: disable-next=home-assistant-duplicate-const CONF_USERNAME: Final = "username" +# pylint: disable-next=home-assistant-duplicate-const CONF_PASSWORD: Final = "password" CONF_PRIVATE_KEY_FILE: Final = "private_key_file" CONF_BACKUP_LOCATION: Final = "backup_location" diff --git a/homeassistant/components/skybeacon/sensor.py b/homeassistant/components/skybeacon/sensor.py index 9d460c815100..abe03a45e2f6 100644 --- a/homeassistant/components/skybeacon/sensor.py +++ b/homeassistant/components/skybeacon/sensor.py @@ -30,6 +30,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType _LOGGER = logging.getLogger(__name__) ATTR_DEVICE = "device" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL = "model" BLE_TEMP_HANDLE = 0x24 diff --git a/homeassistant/components/sleepiq/const.py b/homeassistant/components/sleepiq/const.py index 0efb8e94ebe5..ac15986562f4 100644 --- a/homeassistant/components/sleepiq/const.py +++ b/homeassistant/components/sleepiq/const.py @@ -11,6 +11,7 @@ FIRMNESS = "firmness" ICON_EMPTY = "mdi:bed-empty" ICON_OCCUPIED = "mdi:bed" IS_IN_BED = "is_in_bed" +# pylint: disable-next=home-assistant-duplicate-const PRESSURE = "pressure" SLEEP_NUMBER = "sleep_number" FOOT_WARMING_TIMER = "foot_warming_timer" diff --git a/homeassistant/components/slide_local/const.py b/homeassistant/components/slide_local/const.py index 9dc6d4ac9257..168bb3679832 100644 --- a/homeassistant/components/slide_local/const.py +++ b/homeassistant/components/slide_local/const.py @@ -3,6 +3,7 @@ API_LOCAL = "api_local" ATTR_TOUCHGO = "touchgo" CONF_INVERT_POSITION = "invert_position" +# pylint: disable-next=home-assistant-duplicate-const CONF_VERIFY_SSL = "verify_ssl" DOMAIN = "slide_local" SLIDES = "slides" diff --git a/homeassistant/components/smarttub/sensor.py b/homeassistant/components/smarttub/sensor.py index 735229079a42..1e43e5f5674b 100644 --- a/homeassistant/components/smarttub/sensor.py +++ b/homeassistant/components/smarttub/sensor.py @@ -19,6 +19,7 @@ from .entity import SmartTubOnboardSensorBase # the desired duration, in hours, of the cycle ATTR_DURATION = "duration" ATTR_CYCLE_LAST_UPDATED = "cycle_last_updated" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODE = "mode" # the hour of the day at which to start the cycle (0-23) ATTR_START_HOUR = "start_hour" diff --git a/homeassistant/components/smtp/const.py b/homeassistant/components/smtp/const.py index 1fa077a24fb8..9dd60b8bed17 100644 --- a/homeassistant/components/smtp/const.py +++ b/homeassistant/components/smtp/const.py @@ -9,6 +9,7 @@ ATTR_HTML: Final = "html" ATTR_SENDER_NAME: Final = "sender_name" CONF_ENCRYPTION: Final = "encryption" +# pylint: disable-next=home-assistant-duplicate-const CONF_DEBUG: Final = "debug" CONF_SERVER: Final = "server" CONF_SENDER_NAME: Final = "sender_name" diff --git a/homeassistant/components/squeezebox/const.py b/homeassistant/components/squeezebox/const.py index d1e80e4a48fe..f4e882e05411 100644 --- a/homeassistant/components/squeezebox/const.py +++ b/homeassistant/components/squeezebox/const.py @@ -51,6 +51,7 @@ ATTR_DAYS_OF_WEEK = "dow" ATTR_ENABLED = "enabled" ATTR_REPEAT = "repeat" ATTR_SCHEDULED_TODAY = "scheduled_today" +# pylint: disable-next=home-assistant-duplicate-const ATTR_TIME = "time" ATTR_VOLUME = "volume" ATTR_URL = "url" diff --git a/homeassistant/components/subaru/const.py b/homeassistant/components/subaru/const.py index 0ff9d6bec2cb..d751471cc5a3 100644 --- a/homeassistant/components/subaru/const.py +++ b/homeassistant/components/subaru/const.py @@ -38,9 +38,11 @@ PLATFORMS = [ Platform.SENSOR, ] +# pylint: disable-next=home-assistant-duplicate-const SERVICE_LOCK = "lock" SERVICE_REMOTE_START = "remote_start" SERVICE_REMOTE_STOP = "remote_stop" +# pylint: disable-next=home-assistant-duplicate-const SERVICE_UNLOCK = "unlock" SERVICE_UNLOCK_SPECIFIC_DOOR = "unlock_specific_door" diff --git a/homeassistant/components/surepetcare/const.py b/homeassistant/components/surepetcare/const.py index e0940b410025..e01f2d5e51a6 100644 --- a/homeassistant/components/surepetcare/const.py +++ b/homeassistant/components/surepetcare/const.py @@ -18,6 +18,7 @@ SURE_BATT_VOLTAGE_DIFF = SURE_BATT_VOLTAGE_FULL - SURE_BATT_VOLTAGE_LOW SERVICE_SET_LOCK_STATE = "set_lock_state" SERVICE_SET_PET_LOCATION = "set_pet_location" ATTR_FLAP_ID = "flap_id" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LOCATION = "location" ATTR_LOCK_STATE = "lock_state" ATTR_PET_NAME = "pet_name" diff --git a/homeassistant/components/telegram_bot/const.py b/homeassistant/components/telegram_bot/const.py index 7079cd2dc84b..6d273c382a58 100644 --- a/homeassistant/components/telegram_bot/const.py +++ b/homeassistant/components/telegram_bot/const.py @@ -87,6 +87,7 @@ ATTR_CALLBACK_QUERY_ID = "callback_query_id" ATTR_CAPTION = "caption" ATTR_CHAT_ID = "chat_id" ATTR_CHAT_INSTANCE = "chat_instance" +# pylint: disable-next=home-assistant-duplicate-const ATTR_DATE = "date" ATTR_DISABLE_NOTIF = "disable_notification" ATTR_DISABLE_WEB_PREV = "disable_web_page_preview" diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 5faa27eb0e7a..4be06329ee1c 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -57,6 +57,7 @@ _LOGGER = logging.getLogger(__name__) # Legacy ATTR_COLOR_TEMP = "color_temp" +# pylint: disable-next=home-assistant-duplicate-const CONF_HS = "hs" CONF_HS_ACTION = "set_hs" CONF_RGB_ACTION = "set_rgb" diff --git a/homeassistant/components/template/select.py b/homeassistant/components/template/select.py index 1f8d55e17d0b..b3116728edaf 100644 --- a/homeassistant/components/template/select.py +++ b/homeassistant/components/template/select.py @@ -40,6 +40,7 @@ from .trigger_entity import TriggerEntity _LOGGER = logging.getLogger(__name__) +# pylint: disable-next=home-assistant-duplicate-const CONF_OPTIONS = "options" CONF_SELECT_OPTION = "select_option" diff --git a/homeassistant/components/template/weather.py b/homeassistant/components/template/weather.py index 746be20cfbb9..9f431d872ba8 100644 --- a/homeassistant/components/template/weather.py +++ b/homeassistant/components/template/weather.py @@ -95,6 +95,7 @@ CONF_ATTRIBUTION = "attribution" CONF_ATTRIBUTION_TEMPLATE = "attribution_template" CONF_CLOUD_COVERAGE = "cloud_coverage" CONF_CLOUD_COVERAGE_TEMPLATE = "cloud_coverage_template" +# pylint: disable-next=home-assistant-duplicate-const CONF_CONDITION = "condition" CONF_CONDITION_TEMPLATE = "condition_template" CONF_DEW_POINT = "dew_point" diff --git a/homeassistant/components/tesla_fleet/const.py b/homeassistant/components/tesla_fleet/const.py index 6faf270f1fbf..4f962995409e 100644 --- a/homeassistant/components/tesla_fleet/const.py +++ b/homeassistant/components/tesla_fleet/const.py @@ -7,6 +7,7 @@ from tesla_fleet_api.const import Scope DOMAIN = "tesla_fleet" +# pylint: disable-next=home-assistant-duplicate-const CONF_DOMAIN = "domain" CONF_REFRESH_TOKEN = "refresh_token" diff --git a/homeassistant/components/teslemetry/services.py b/homeassistant/components/teslemetry/services.py index 1fde9038100e..d72fbc6f888a 100644 --- a/homeassistant/components/teslemetry/services.py +++ b/homeassistant/components/teslemetry/services.py @@ -18,16 +18,19 @@ from .models import TeslemetryEnergyData, TeslemetryVehicleData _LOGGER = logging.getLogger(__name__) # Attributes +# pylint: disable-next=home-assistant-duplicate-const ATTR_ID = "id" ATTR_GPS = "gps" ATTR_TYPE = "type" ATTR_VALUE = "value" +# pylint: disable-next=home-assistant-duplicate-const ATTR_LOCATION = "location" ATTR_LOCALE = "locale" ATTR_ORDER = "order" ATTR_TIMESTAMP = "timestamp" ATTR_FIELDS = "fields" ATTR_ENABLE = "enable" +# pylint: disable-next=home-assistant-duplicate-const ATTR_TIME = "time" ATTR_PIN = "pin" ATTR_TOU_SETTINGS = "tou_settings" @@ -41,6 +44,7 @@ ATTR_DAYS_OF_WEEK = "days_of_week" ATTR_START_TIME = "start_time" ATTR_END_TIME = "end_time" ATTR_ONE_TIME = "one_time" +# pylint: disable-next=home-assistant-duplicate-const ATTR_NAME = "name" ATTR_PRECONDITION_TIME = "precondition_time" diff --git a/homeassistant/components/text/const.py b/homeassistant/components/text/const.py index 3670c30120b3..d62446dbf694 100644 --- a/homeassistant/components/text/const.py +++ b/homeassistant/components/text/const.py @@ -4,6 +4,7 @@ DOMAIN = "text" ATTR_MAX = "max" ATTR_MIN = "min" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODE = "mode" ATTR_PATTERN = "pattern" ATTR_VALUE = "value" diff --git a/homeassistant/components/tilt_pi/sensor.py b/homeassistant/components/tilt_pi/sensor.py index 4ce40e70bdb9..5244cfff7503 100644 --- a/homeassistant/components/tilt_pi/sensor.py +++ b/homeassistant/components/tilt_pi/sensor.py @@ -23,6 +23,7 @@ from .entity import TiltEntity # Coordinator is used to centralize the data updates PARALLEL_UPDATES = 0 +# pylint: disable-next=home-assistant-duplicate-const ATTR_TEMPERATURE = "temperature" ATTR_GRAVITY = "gravity" diff --git a/homeassistant/components/time_date/const.py b/homeassistant/components/time_date/const.py index 2163fe76bdb2..28d90016cf13 100644 --- a/homeassistant/components/time_date/const.py +++ b/homeassistant/components/time_date/const.py @@ -4,6 +4,7 @@ from typing import Final from homeassistant.const import Platform +# pylint: disable-next=home-assistant-duplicate-const CONF_DISPLAY_OPTIONS = "display_options" DOMAIN: Final = "time_date" PLATFORMS = [Platform.SENSOR] diff --git a/homeassistant/components/tplink_omada/services.py b/homeassistant/components/tplink_omada/services.py index ebe42271a8ab..7c7fdfbb0b34 100644 --- a/homeassistant/components/tplink_omada/services.py +++ b/homeassistant/components/tplink_omada/services.py @@ -15,6 +15,7 @@ from .controller import OmadaSiteController SERVICE_RECONNECT_CLIENT = "reconnect_client" +# pylint: disable-next=home-assistant-duplicate-const ATTR_CONFIG_ENTRY_ID = "config_entry_id" ATTR_MAC = "mac" diff --git a/homeassistant/components/triggercmd/const.py b/homeassistant/components/triggercmd/const.py index 0fc15b2b806e..59905401c262 100644 --- a/homeassistant/components/triggercmd/const.py +++ b/homeassistant/components/triggercmd/const.py @@ -1,4 +1,5 @@ """Constants for the TRIGGERcmd integration.""" DOMAIN = "triggercmd" +# pylint: disable-next=home-assistant-duplicate-const CONF_TOKEN = "token" diff --git a/homeassistant/components/tuya/const.py b/homeassistant/components/tuya/const.py index 424c9b82e360..cb7ba67c3258 100644 --- a/homeassistant/components/tuya/const.py +++ b/homeassistant/components/tuya/const.py @@ -33,6 +33,7 @@ CONF_ENDPOINT = "endpoint" CONF_TERMINAL_ID = "terminal_id" CONF_TOKEN_INFO = "token_info" CONF_USER_CODE = "user_code" +# pylint: disable-next=home-assistant-duplicate-const CONF_USERNAME = "username" TUYA_CLIENT_ID = "HA_3y9q4ak7g4ephrvke" diff --git a/homeassistant/components/version/const.py b/homeassistant/components/version/const.py index 8180ee8e9997..1c0995a88a98 100644 --- a/homeassistant/components/version/const.py +++ b/homeassistant/components/version/const.py @@ -23,6 +23,7 @@ CONF_BOARD: Final = "board" CONF_CHANNEL: Final = "channel" CONF_IMAGE: Final = "image" CONF_VERSION_SOURCE: Final = "version_source" +# pylint: disable-next=home-assistant-duplicate-const CONF_SOURCE: Final = "source" ATTR_CHANNEL: Final = CONF_CHANNEL diff --git a/homeassistant/components/victron_gx/const.py b/homeassistant/components/victron_gx/const.py index ca806ca5249c..1ca47c0c9963 100644 --- a/homeassistant/components/victron_gx/const.py +++ b/homeassistant/components/victron_gx/const.py @@ -3,6 +3,7 @@ DOMAIN = "victron_gx" CONF_INSTALLATION_ID = "installation_id" +# pylint: disable-next=home-assistant-duplicate-const CONF_MODEL = "model" CONF_SERIAL = "serial" diff --git a/homeassistant/components/victron_remote_monitoring/const.py b/homeassistant/components/victron_remote_monitoring/const.py index 3de1dbcabb25..eb2f3925064f 100644 --- a/homeassistant/components/victron_remote_monitoring/const.py +++ b/homeassistant/components/victron_remote_monitoring/const.py @@ -6,4 +6,5 @@ DOMAIN = "victron_remote_monitoring" LOGGER = logging.getLogger(__package__) CONF_SITE_ID = "site_id" +# pylint: disable-next=home-assistant-duplicate-const CONF_API_TOKEN = "api_token" diff --git a/homeassistant/components/wiz/config_flow.py b/homeassistant/components/wiz/config_flow.py index a120ce875f56..5738895fa8e1 100644 --- a/homeassistant/components/wiz/config_flow.py +++ b/homeassistant/components/wiz/config_flow.py @@ -21,6 +21,7 @@ from .utils import _short_mac, name_from_bulb_type_and_mac _LOGGER = logging.getLogger(__name__) +# pylint: disable-next=home-assistant-duplicate-const CONF_DEVICE = "device" diff --git a/homeassistant/components/xiaomi_miio/select.py b/homeassistant/components/xiaomi_miio/select.py index b6caa5872317..64a3e65dca0a 100644 --- a/homeassistant/components/xiaomi_miio/select.py +++ b/homeassistant/components/xiaomi_miio/select.py @@ -65,6 +65,7 @@ from .typing import XiaomiMiioConfigEntry ATTR_DISPLAY_ORIENTATION = "display_orientation" ATTR_LED_BRIGHTNESS = "led_brightness" ATTR_PTC_LEVEL = "ptc_level" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODE = "mode" _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/xiaomi_miio/switch.py b/homeassistant/components/xiaomi_miio/switch.py index 278f7bd0ade8..4231f3c03ff7 100644 --- a/homeassistant/components/xiaomi_miio/switch.py +++ b/homeassistant/components/xiaomi_miio/switch.py @@ -149,6 +149,7 @@ ATTR_LED = "led" ATTR_IONIZER = "ionizer" ATTR_ANION = "anion" ATTR_LOAD_POWER = "load_power" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MODEL = "model" ATTR_POWER = "power" ATTR_POWER_MODE = "power_mode" diff --git a/homeassistant/components/xthings_cloud/const.py b/homeassistant/components/xthings_cloud/const.py index 09535b588b17..0fdcf725e858 100644 --- a/homeassistant/components/xthings_cloud/const.py +++ b/homeassistant/components/xthings_cloud/const.py @@ -7,10 +7,14 @@ from homeassistant.const import Platform DOMAIN = "xthings_cloud" LOGGER = logging.getLogger(__package__) +# pylint: disable-next=home-assistant-duplicate-const CONF_EMAIL = "email" +# pylint: disable-next=home-assistant-duplicate-const CONF_PASSWORD = "password" +# pylint: disable-next=home-assistant-duplicate-const CONF_TOKEN = "token" CONF_REFRESH_TOKEN = "refresh_token" +# pylint: disable-next=home-assistant-duplicate-const CONF_CLIENT_ID = "client_id" CONF_INSTANCE_ID = "instance_id" diff --git a/homeassistant/components/zeroconf/discovery.py b/homeassistant/components/zeroconf/discovery.py index 4b2366fd76ff..d70a27bc2174 100644 --- a/homeassistant/components/zeroconf/discovery.py +++ b/homeassistant/components/zeroconf/discovery.py @@ -41,7 +41,9 @@ HOMEKIT_PAIRED_STATUS_FLAG = "sf" HOMEKIT_MODEL_LOWER = "md" HOMEKIT_MODEL_UPPER = "MD" +# pylint: disable-next=home-assistant-duplicate-const ATTR_DOMAIN: Final = "domain" +# pylint: disable-next=home-assistant-duplicate-const ATTR_NAME: Final = "name" ATTR_PROPERTIES: Final = "properties" diff --git a/homeassistant/components/zha/const.py b/homeassistant/components/zha/const.py index b50a67898e8e..d220937028a4 100644 --- a/homeassistant/components/zha/const.py +++ b/homeassistant/components/zha/const.py @@ -13,6 +13,7 @@ ATTR_ENDPOINT_NAMES = "endpoint_names" ATTR_IEEE = "ieee" ATTR_LAST_SEEN = "last_seen" ATTR_LQI = "lqi" +# pylint: disable-next=home-assistant-duplicate-const ATTR_MANUFACTURER = "manufacturer" ATTR_MANUFACTURER_CODE = "manufacturer_code" ATTR_NEIGHBORS = "neighbors" diff --git a/pylint/plugins/pylint_home_assistant/checkers/duplicate_const.py b/pylint/plugins/pylint_home_assistant/checkers/duplicate_const.py new file mode 100644 index 000000000000..e3999dc73764 --- /dev/null +++ b/pylint/plugins/pylint_home_assistant/checkers/duplicate_const.py @@ -0,0 +1,138 @@ +"""Checker for integration constants that duplicate homeassistant.const. + +When an integration defines a constant like ``CONF_HOST = "host"`` or +``CONF_HOST: Final = "host"`` that already exists with the same name +and value in ``homeassistant.const``, the integration should import it +instead of redefining it. +""" + +import astroid +from astroid import nodes +from pylint.checkers import BaseChecker +from pylint.lint import PyLinter + +from pylint_home_assistant.helpers.module_info import is_integration_module + +_ha_consts: dict[str, str] | None = None + + +def _load_ha_consts() -> dict[str, str]: + """Load string constants from homeassistant.const. + + Returns a dict mapping constant name to its string value. + Collects both annotated (``NAME: Final = "value"``) and plain + (``NAME = "value"``) assignment forms. + """ + global _ha_consts # noqa: PLW0603 + if _ha_consts is not None: + return _ha_consts + + _ha_consts = {} + try: + module = astroid.MANAGER.ast_from_module_name("homeassistant.const") + except astroid.exceptions.AstroidBuildingError: + return _ha_consts + + for item in module.body: + name: str | None = None + value_node: nodes.NodeNG | None = None + + if isinstance(item, nodes.AnnAssign) and isinstance( + item.target, nodes.AssignName + ): + name = item.target.name + value_node = item.value + elif isinstance(item, nodes.Assign) and len(item.targets) == 1: + target = item.targets[0] + if isinstance(target, nodes.AssignName): + name = target.name + value_node = item.value + + if ( + name is not None + and isinstance(value_node, nodes.Const) + and isinstance(value_node.value, str) + ): + _ha_consts[name] = value_node.value + + return _ha_consts + + +def _check_assignment( + checker: DuplicateConstChecker, + node: nodes.NodeNG, + name: str, + value_node: nodes.NodeNG | None, +) -> None: + """Check a single assignment for duplicate constants.""" + if not isinstance(node.parent, nodes.Module): + return + + if value_node is None: + return + + if not isinstance(value_node, nodes.Const): + return + + if not isinstance(value_node.value, str): + return + + ha_consts = _load_ha_consts() + if name in ha_consts and ha_consts[name] == value_node.value: + checker.add_message( + "home-assistant-duplicate-const", + node=node, + args=(name,), + ) + + +class DuplicateConstChecker(BaseChecker): + """Checker for constants that duplicate homeassistant.const entries.""" + + name = "home_assistant_duplicate_const" + priority = -1 + msgs = { + "C7413": ( + "'%s' is already defined in homeassistant.const with the same " + "value, import it instead of redefining it", + "home-assistant-duplicate-const", + "Used when an integration defines a constant that already exists " + "with the same name and value in homeassistant.const.", + ), + } + options = () + + _in_integration: bool + + def visit_module(self, node: nodes.Module) -> None: + """Track whether we are in an integration module.""" + self._in_integration = is_integration_module(node.name) + + def visit_assign(self, node: nodes.Assign) -> None: + """Check plain assignments like ``NAME = 'value'``.""" + if not self._in_integration: + return + + if len(node.targets) != 1: + return + + target = node.targets[0] + if not isinstance(target, nodes.AssignName): + return + + _check_assignment(self, node, target.name, node.value) + + def visit_annassign(self, node: nodes.AnnAssign) -> None: + """Check annotated assignments like ``NAME: Final = 'value'``.""" + if not self._in_integration: + return + + if not isinstance(node.target, nodes.AssignName): + return + + _check_assignment(self, node, node.target.name, node.value) + + +def register(linter: PyLinter) -> None: + """Register the checker.""" + linter.register_checker(DuplicateConstChecker(linter)) diff --git a/tests/pylint/test_duplicate_const.py b/tests/pylint/test_duplicate_const.py new file mode 100644 index 000000000000..845d9c5fe848 --- /dev/null +++ b/tests/pylint/test_duplicate_const.py @@ -0,0 +1,149 @@ +"""Tests for the duplicate const checker.""" + +import astroid +from pylint.testutils import UnittestLinter +from pylint.utils.ast_walker import ASTWalker +from pylint_home_assistant.checkers.duplicate_const import DuplicateConstChecker +import pytest + +from . import assert_no_messages + +# Pre-load homeassistant.const so astroid can resolve it. +astroid.MANAGER.ast_from_module_name("homeassistant.const") + + +@pytest.fixture(name="duplicate_const_checker") +def duplicate_const_checker_fixture( + linter: UnittestLinter, +) -> DuplicateConstChecker: + """Fixture to provide a duplicate const checker.""" + return DuplicateConstChecker(linter) + + +@pytest.mark.parametrize( + "code", + [ + pytest.param( + """ +DOMAIN = "my_integration" +""", + id="domain_same_value_different_semantics", + ), + pytest.param( + """ +MY_CUSTOM_CONST = "host" +""", + id="different_name_same_value", + ), + pytest.param( + """ +CONF_HOST = "my_custom_host" +""", + id="same_name_different_value", + ), + pytest.param( + """ +CONF_SPECIAL_SETTING = "special" +""", + id="name_not_in_ha_const", + ), + pytest.param( + """ +from typing import Final + +CONF_HOST: Final = "my_custom_host" +""", + id="annotated_different_value", + ), + ], +) +def test_no_warning( + linter: UnittestLinter, + duplicate_const_checker: DuplicateConstChecker, + code: str, +) -> None: + """Test cases that should not trigger a warning.""" + root_node = astroid.parse(code, "homeassistant.components.test_integration.const") + walker = ASTWalker(linter) + walker.add_checker(duplicate_const_checker) + + with assert_no_messages(linter): + walker.walk(root_node) + + +@pytest.mark.parametrize( + ("name", "code"), + [ + pytest.param( + "CONF_HOST", + """ +CONF_HOST = "host" +""", + id="conf_host", + ), + pytest.param( + "CONF_PASSWORD", + """ +CONF_PASSWORD = "password" +""", + id="conf_password", + ), + pytest.param( + "ATTR_TEMPERATURE", + """ +ATTR_TEMPERATURE = "temperature" +""", + id="attr_temperature", + ), + pytest.param( + "CONF_HOST", + """ +from typing import Final + +CONF_HOST: Final = "host" +""", + id="annotated_final", + ), + pytest.param( + "CONF_API_KEY", + """ +CONF_API_KEY: str = "api_key" +""", + id="annotated_str", + ), + ], +) +def test_duplicate_const_flagged( + linter: UnittestLinter, + duplicate_const_checker: DuplicateConstChecker, + name: str, + code: str, +) -> None: + """Test that duplicate constants are flagged.""" + root_node = astroid.parse(code, "homeassistant.components.test_integration.const") + walker = ASTWalker(linter) + walker.add_checker(duplicate_const_checker) + walker.walk(root_node) + + messages = linter.release_messages() + assert len(messages) == 1 + assert messages[0].msg_id == "home-assistant-duplicate-const" + assert messages[0].args == (name,) + + +def test_not_integration_ignored( + linter: UnittestLinter, + duplicate_const_checker: DuplicateConstChecker, +) -> None: + """Test that non-integration modules are ignored.""" + root_node = astroid.parse( + """ +CONF_HOST = "host" +""", + "tests.components.test_integration.test_const", + ) + walker = ASTWalker(linter) + walker.add_checker(duplicate_const_checker) + + with assert_no_messages(linter): + walker.walk(root_node)