From 850f7f6bfda3d982c9b6ceaf905c84c018fabfbd Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Mon, 22 Jan 2024 20:19:46 +0000 Subject: [PATCH] Added initial translations to French and Italian --- README.md | 5 + custom_components/nissan_connect/base.py | 13 +- .../nissan_connect/binary_sensor.py | 11 +- custom_components/nissan_connect/button.py | 10 +- custom_components/nissan_connect/climate.py | 2 +- .../nissan_connect/device_tracker.py | 2 +- custom_components/nissan_connect/sensor.py | 50 +++---- .../nissan_connect/translations/en.json | 85 +++++++++++- .../nissan_connect/translations/fr.json | 128 ++++++++++++++++++ .../nissan_connect/translations/it.json | 128 ++++++++++++++++++ 10 files changed, 383 insertions(+), 51 deletions(-) create mode 100644 custom_components/nissan_connect/translations/fr.json create mode 100644 custom_components/nissan_connect/translations/it.json diff --git a/README.md b/README.md index 9e5a3a8..4e49f5c 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,11 @@ From the Home Assistant Integrations page, search for and add the Nissan Connect ## Update Time Following the model of leaf2mqtt, this integration can be set to use a different update time when plugged in. When HVAC is turned on the update time drops to once per minute. +## Translations +I have provided machine translations from English to the following languages as a start, but welcome native speakers to give feedback on these and to improve them: +* French +* Italian + ## Entities This integration exposes the following entities. Please note that entities will only be shown if the functionality is supported by your car. diff --git a/custom_components/nissan_connect/base.py b/custom_components/nissan_connect/base.py index 77b58a7..9704511 100644 --- a/custom_components/nissan_connect/base.py +++ b/custom_components/nissan_connect/base.py @@ -5,6 +5,8 @@ class KamereonEntity(Entity): """Base class for all Kamereon car entities.""" + _attr_has_entity_name = True + def __init__(self, cooordinator, vehicle): """Initialize the entity.""" self.vehicle = vehicle @@ -32,19 +34,10 @@ def icon(self): def _vehicle_name(self): return self.vehicle.nickname or self.vehicle.model_name - @property - def name(self): - """Return full name of the entity.""" - if not self._attr_name: - return self._vehicle_name - return f"{self._vehicle_name} {self._attr_name}" - @property def unique_id(self): """Return unique ID of the entity.""" - if not self._attr_name: - return None - return f"{self._vehicle_name}_{self._attr_name}" + return f"{self._vehicle_name}_{self._attr_translation_key}" @property def device_info(self): diff --git a/custom_components/nissan_connect/binary_sensor.py b/custom_components/nissan_connect/binary_sensor.py index 125ed70..c977b44 100644 --- a/custom_components/nissan_connect/binary_sensor.py +++ b/custom_components/nissan_connect/binary_sensor.py @@ -1,6 +1,4 @@ """Support for Kamereon cars.""" -import logging - from homeassistant.components.binary_sensor import BinarySensorEntity, BinarySensorDeviceClass from homeassistant.const import STATE_UNKNOWN @@ -8,9 +6,6 @@ from .kamereon import ChargingStatus, PluggedStatus, LockStatus, Feature from .const import DOMAIN, DATA_VEHICLES, DATA_COORDINATOR -_LOGGER = logging.getLogger(__name__) - - async def async_setup_entry(hass, config, async_add_entities): """Set up the Kamereon sensors.""" data = hass.data[DOMAIN][DATA_VEHICLES] @@ -31,7 +26,7 @@ async def async_setup_entry(hass, config, async_add_entities): class ChargingStatusEntity(KamereonEntity, BinarySensorEntity): """Representation of charging status.""" _attr_device_class = BinarySensorDeviceClass.BATTERY_CHARGING - _attr_name = "Charging" + _attr_translation_key = "charging" @property def icon(self): @@ -58,7 +53,7 @@ def device_state_attributes(self): class PluggedStatusEntity(KamereonEntity, BinarySensorEntity): """Representation of plugged status.""" _attr_device_class = BinarySensorDeviceClass.PLUG - _attr_name = "Plugged In" + _attr_translation_key = "plugged" @property def icon(self): @@ -85,7 +80,7 @@ def device_state_attributes(self): class LockStatusEntity(KamereonEntity, BinarySensorEntity): _attr_device_class = BinarySensorDeviceClass.LOCK - _attr_name = "Doors Locked" + _attr_translation_key = "doors_locked" @property def icon(self): diff --git a/custom_components/nissan_connect/button.py b/custom_components/nissan_connect/button.py index a0320b8..11e0201 100644 --- a/custom_components/nissan_connect/button.py +++ b/custom_components/nissan_connect/button.py @@ -21,8 +21,8 @@ async def async_setup_entry(hass, config, async_add_entities): entities.append(ForceUpdateButton(coordinator, data[vehicle], hass, stats_coordinator)) if Feature.HORN_AND_LIGHTS in data[vehicle].features: entities += [ - HornLightsButtons(coordinator, data[vehicle], "Flash Lights", "mdi:car-light-high", "lights"), - HornLightsButtons(coordinator, data[vehicle], "Honk Horn", "mdi:bullhorn", "horn_lights") + HornLightsButtons(coordinator, data[vehicle], "flash_lights", "mdi:car-light-high", "lights"), + HornLightsButtons(coordinator, data[vehicle], "honk_horn", "mdi:bullhorn", "horn_lights") ] @@ -30,7 +30,7 @@ async def async_setup_entry(hass, config, async_add_entities): class ForceUpdateButton(KamereonEntity, ButtonEntity): - _attr_name = "Update Data" + _attr_translation_key = "update_data" def __init__(self, coordinator, vehicle, hass, stats_coordinator): KamereonEntity.__init__(self, coordinator, vehicle) @@ -47,8 +47,8 @@ async def async_press(self): await self.coordinator_statistics.async_refresh() class HornLightsButtons(KamereonEntity, ButtonEntity): - def __init__(self, coordinator, vehicle, name, icon, action): - self._attr_name = name + def __init__(self, coordinator, vehicle, translation_key, icon, action): + self._attr_translation_key = translation_key self._icon = icon self._action = action KamereonEntity.__init__(self, coordinator, vehicle) diff --git a/custom_components/nissan_connect/climate.py b/custom_components/nissan_connect/climate.py index b3294a8..fa8994e 100644 --- a/custom_components/nissan_connect/climate.py +++ b/custom_components/nissan_connect/climate.py @@ -32,7 +32,7 @@ class KamereonClimate(KamereonEntity, ClimateEntity): _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE _attr_hvac_modes = SUPPORT_HVAC _attr_temperature_unit = UnitOfTemperature.CELSIUS - _attr_name = "Climate" + _attr_translation_key = "climate" _attr_min_temp = 16 _attr_max_temp = 26 _attr_target_temperature_step = 1 diff --git a/custom_components/nissan_connect/device_tracker.py b/custom_components/nissan_connect/device_tracker.py index 2ef65af..fb48416 100644 --- a/custom_components/nissan_connect/device_tracker.py +++ b/custom_components/nissan_connect/device_tracker.py @@ -24,7 +24,7 @@ async def async_setup_entry(hass, entry, async_add_entities): return True class KamereonDeviceTracker(KamereonEntity, TrackerEntity): - _attr_name = "Location" + _attr_translation_key = "location" @property def latitude(self) -> float: diff --git a/custom_components/nissan_connect/sensor.py b/custom_components/nissan_connect/sensor.py index b4d664a..cece8db 100644 --- a/custom_components/nissan_connect/sensor.py +++ b/custom_components/nissan_connect/sensor.py @@ -31,22 +31,22 @@ async def async_setup_entry(hass, config, async_add_entities): RangeSensor(coordinator, data[vehicle], False, imperial_distance), ChargeTimeRequiredSensor(coordinator, data[vehicle], ChargingSpeed.NORMAL), ChargeTimeRequiredSensor(coordinator, data[vehicle], ChargingSpeed.FAST), - TimestampSensor(coordinator, data[vehicle], 'battery_status_last_updated', 'Last Updated', 'mdi:clock-time-eleven-outline')] + TimestampSensor(coordinator, data[vehicle], 'battery_status_last_updated', 'last_updated', 'mdi:clock-time-eleven-outline')] if data[vehicle].internal_temperature is not None: entities.append(InternalTemperatureSensor(coordinator, data[vehicle])) if data[vehicle].external_temperature is not None: entities.append(ExternalTemperatureSensor(coordinator, data[vehicle])) if Feature.DRIVING_JOURNEY_HISTORY in data[vehicle].features: entities += [ - StatisticSensor(coordinator_stats, data[vehicle], 'daily', lambda x: x.total_distance, 'Daily Distance', 'mdi:map-marker-distance', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 0, imperial_distance), - StatisticSensor(coordinator_stats, data[vehicle], 'daily', lambda x: x.trip_count, 'Daily Trips', 'mdi:hiking', None, None, 0), - StatisticSensor(coordinator_stats, data[vehicle], 'monthly', lambda x: x.total_distance, 'Monthly Distance', 'mdi:map-marker-distance', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 0, imperial_distance), - StatisticSensor(coordinator_stats, data[vehicle], 'monthly', lambda x: x.trip_count, 'Monthly Trips', 'mdi:hiking', None, None, 0), + StatisticSensor(coordinator_stats, data[vehicle], 'daily', lambda x: x.total_distance, 'daily_distance', 'mdi:map-marker-distance', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 0, imperial_distance), + StatisticSensor(coordinator_stats, data[vehicle], 'daily', lambda x: x.trip_count, 'daily_trips', 'mdi:hiking', None, None, 0), + StatisticSensor(coordinator_stats, data[vehicle], 'monthly', lambda x: x.total_distance, 'monthly_distance', 'mdi:map-marker-distance', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 0, imperial_distance), + StatisticSensor(coordinator_stats, data[vehicle], 'monthly', lambda x: x.trip_count, 'monthly_trips', 'mdi:hiking', None, None, 0), ] if Feature.BATTERY_STATUS in data[vehicle].features: entities += [ - StatisticSensor(coordinator_stats, data[vehicle], 'daily', lambda x: x.total_distance / x.consumed_electricity, 'Daily Efficiency', 'mdi:ev-station', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 2, imperial_distance), - StatisticSensor(coordinator_stats, data[vehicle], 'monthly', lambda x: x.total_distance / x.consumed_electricity, 'Monthly Efficiency', 'mdi:ev-station', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 2, imperial_distance), + StatisticSensor(coordinator_stats, data[vehicle], 'daily', lambda x: x.total_distance / x.consumed_electricity, 'daily_efficiency', 'mdi:ev-station', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 2, imperial_distance), + StatisticSensor(coordinator_stats, data[vehicle], 'monthly', lambda x: x.total_distance / x.consumed_electricity, 'monthly_efficiency', 'mdi:ev-station', SensorDeviceClass.DISTANCE, UnitOfLength.KILOMETERS, 2, imperial_distance), ] entities.append(OdometerSensor(coordinator, data[vehicle], imperial_distance)) @@ -55,10 +55,13 @@ async def async_setup_entry(hass, config, async_add_entities): class BatteryLevelSensor(KamereonEntity, SensorEntity): - _attr_name = "Battery Level" + _attr_translation_key = "battery_level" _attr_device_class = SensorDeviceClass.BATTERY _attr_native_unit_of_measurement = PERCENTAGE + def __init__(self, coordinator, vehicle): + KamereonEntity.__init__(self, coordinator, vehicle) + @property def state(self): """Return the state.""" @@ -80,7 +83,7 @@ def device_state_attributes(self): class InternalTemperatureSensor(KamereonEntity, SensorEntity): - _attr_name = "Internal Temperature" + _attr_translation_key = "internal_temperature" _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS @@ -106,7 +109,7 @@ def device_state_attributes(self): class ExternalTemperatureSensor(KamereonEntity, SensorEntity): - _attr_name = "External Temperature" + _attr_translation_key = "external_temperature" _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS @@ -132,7 +135,6 @@ def device_state_attributes(self): class RangeSensor(KamereonEntity, SensorEntity): - _attr_name = "Range" _attr_device_class = SensorDeviceClass.DISTANCE _attr_native_unit_of_measurement = UnitOfLength.KILOMETERS @@ -140,7 +142,7 @@ def __init__(self, coordinator, vehicle, hvac, imperial_distance): if imperial_distance: self._attr_suggested_unit_of_measurement = UnitOfLength.MILES - self._attr_name = "Range (AC On)" if hvac else "Range (AC Off)" + self._attr_translation_key = "range_ac_on" if hvac else "range_ac_off" KamereonEntity.__init__(self, coordinator, vehicle) self.hvac = hvac @@ -158,7 +160,7 @@ def icon(self): class OdometerSensor(KamereonEntity, SensorEntity): - _attr_name = "Odometer" + _attr_translation_key = "odometer" _attr_device_class = SensorDeviceClass.DISTANCE _attr_native_unit_of_measurement = UnitOfLength.KILOMETERS @@ -179,13 +181,13 @@ def icon(self): class StatisticSensor(KamereonEntity, SensorEntity): - def __init__(self, coordinator, vehicle, key, func, name, icon, device_class, unit, precision, imperial_distance=False): + def __init__(self, coordinator, vehicle, key, func, translation_key, icon, device_class, unit, precision, imperial_distance=False): self._attr_device_class = device_class self._attr_native_unit_of_measurement = unit self._attr_suggested_display_precision = precision if imperial_distance: self._attr_suggested_unit_of_measurement = UnitOfLength.MILES - self._attr_name = name + self._attr_translation_key = translation_key self._icon = icon self._key = key self._lambda = func @@ -227,21 +229,21 @@ def icon(self): class ChargeTimeRequiredSensor(KamereonEntity, SensorEntity): - _attr_name = "Charge Time" + _attr_translation_key = "charge_time" _attr_device_class = SensorDeviceClass.DURATION _attr_native_unit_of_measurement = UnitOfTime.MINUTES CHARGING_SPEED_NAME = { - ChargingSpeed.FASTEST: '50kW', - ChargingSpeed.FAST: '6kW', - ChargingSpeed.NORMAL: '3kW', - ChargingSpeed.SLOW: '1kW', + ChargingSpeed.FASTEST: '50kw', + ChargingSpeed.FAST: '6kw', + ChargingSpeed.NORMAL: '3kw', + ChargingSpeed.SLOW: '1kw', } def __init__(self, coordinator, vehicle, charging_speed): - self._attr_name = f"Charge Time ({self.CHARGING_SPEED_NAME[charging_speed]})" - KamereonEntity.__init__(self, coordinator, vehicle) self.charging_speed = charging_speed + self._attr_translation_key = "charge_time_" + self.CHARGING_SPEED_NAME[charging_speed] + KamereonEntity.__init__(self, coordinator, vehicle) @property def native_value(self): @@ -257,8 +259,8 @@ def icon(self): class TimestampSensor(KamereonEntity, SensorEntity): _attr_device_class = SensorDeviceClass.TIMESTAMP - def __init__(self, coordinator, vehicle, attribute, name, icon): - self._attr_name = name + def __init__(self, coordinator, vehicle, attribute, translation_key, icon): + self._attr_translation_key = translation_key self._icon = icon KamereonEntity.__init__(self, coordinator, vehicle) self.attribute = attribute diff --git a/custom_components/nissan_connect/translations/en.json b/custom_components/nissan_connect/translations/en.json index 5110791..4a62078 100644 --- a/custom_components/nissan_connect/translations/en.json +++ b/custom_components/nissan_connect/translations/en.json @@ -43,5 +43,86 @@ }, "abort": {} }, - "issues": {} -} \ No newline at end of file + "issues": {}, + "entity": { + "binary_sensor": { + "charging": { + "name": "Charging" + }, + "doors_locked": { + "name": "Doors Locked" + }, + "plugged": { + "name": "Plugged In" + } + }, + "button": { + "flash_lights": { + "name": "Flash Lights" + }, + "honk_horn": { + "name": "Honk Horn" + }, + "update_data": { + "name": "Update Data" + } + }, + "climate": { + "climate": { + "name": "Climate" + } + }, + "device_tracker": { + "location": { + "name": "Location" + } + }, + "sensor": { + "battery_level": { + "name": "Battery Level" + }, + "internal_temperature": { + "name": "Internal Temperature" + }, + "external_temperature": { + "name": "External Temperature" + }, + "range_ac_on": { + "name": "Range (AC On)" + }, + "range_ac_off": { + "name": "Range (AC Off)" + }, + "odometer": { + "name": "Odometer" + }, + "charge_time_3kw": { + "name": "Charge Time (3kW)" + }, + "charge_time_6kw": { + "name": "Charge Time (6kW)" + }, + "last_updated": { + "name": "Last Updated" + }, + "daily_distance": { + "name": "Daily Distance" + }, + "daily_efficiency": { + "name": "Daily Efficiency" + }, + "daily_trips": { + "name": "Daily Trips" + }, + "monthly_distance": { + "name": "Monthly Distance" + }, + "monthly_efficiency": { + "name": "Monthly Efficiency" + }, + "monthly_trips": { + "name": "Monthly Trips" + } + } + } +} diff --git a/custom_components/nissan_connect/translations/fr.json b/custom_components/nissan_connect/translations/fr.json new file mode 100644 index 0000000..3a20097 --- /dev/null +++ b/custom_components/nissan_connect/translations/fr.json @@ -0,0 +1,128 @@ +{ + "title": "NissanConnect", + "config": { + "step": { + "user": { + "description": "Configurez votre compte NissanConnect.", + "data": { + "email": "Adresse e-mail", + "password": "Mot de passe", + "interval": "Intervalle de mise à jour (minutes)", + "interval_charging": "Intervalle de mise à jour pendant la charge (minutes)", + "interval_statistics": "Intervalle de mise à jour des statistiques quotidiennes/mensuelles (minutes)", + "region": "Région (EU, US)", + "imperial_distance": "Utiliser les unités de distance impériales" + } + } + }, + "error": { + "auth_error": "Informations d'identification invalides.", + "region_error": "Région non valide fournie. Veuillez fournir une région valide." + }, + "abort": {} + }, + "options": { + "step": { + "init": { + "description": "Mettez à jour les informations de votre compte NissanConnect.", + "data": { + "email": "Adresse e-mail", + "password": "Mot de passe", + "interval": "Intervalle de mise à jour (minutes)", + "interval_charging": "Intervalle de mise à jour pendant la charge (minutes)", + "interval_statistics": "Intervalle de mise à jour des statistiques quotidiennes/mensuelles (minutes)", + "imperial_distance": "Utiliser les unités de distance impériales" + }, + "data_description": { + "password": "Si vous ne changez pas vos informations d'identification, laissez le champ du mot de passe vide." + } + } + }, + "error": { + "auth_error": "Informations d'identification invalides." + }, + "abort": {} + }, + "issues": {}, + "entity": { + "binary_sensor": { + "charging": { + "name": "En charge" + }, + "doors_locked": { + "name": "Portes verrouillées" + }, + "plugged": { + "name": "Branché" + } + }, + "button": { + "flash_lights": { + "name": "Clignoter les feux" + }, + "honk_horn": { + "name": "Biper le klaxon" + }, + "update_data": { + "name": "Mettre à jour les données" + } + }, + "climate": { + "climate": { + "name": "Climatisation" + } + }, + "device_tracker": { + "location": { + "name": "Emplacement" + } + }, + "sensor": { + "battery_level": { + "name": "Niveau de batterie" + }, + "internal_temperature": { + "name": "Température interne" + }, + "external_temperature": { + "name": "Température externe" + }, + "range_ac_on": { + "name": "Autonomie (AC activé)" + }, + "range_ac_off": { + "name": "Autonomie (AC désactivé)" + }, + "odometer": { + "name": "Compteur kilométrique" + }, + "charge_time_3kw": { + "name": "Temps de charge (3 kW)" + }, + "charge_time_6kw": { + "name": "Temps de charge (6 kW)" + }, + "last_updated": { + "name": "Dernière mise à jour" + }, + "daily_distance": { + "name": "Distance quotidienne" + }, + "daily_efficiency": { + "name": "Efficacité quotidienne" + }, + "daily_trips": { + "name": "Trajets quotidiens" + }, + "monthly_distance": { + "name": "Distance mensuelle" + }, + "monthly_efficiency": { + "name": "Efficacité mensuelle" + }, + "monthly_trips": { + "name": "Trajets mensuels" + } + } + } +} diff --git a/custom_components/nissan_connect/translations/it.json b/custom_components/nissan_connect/translations/it.json new file mode 100644 index 0000000..6db3b18 --- /dev/null +++ b/custom_components/nissan_connect/translations/it.json @@ -0,0 +1,128 @@ +{ + "title": "NissanConnect", + "config": { + "step": { + "user": { + "description": "Configura il tuo account NissanConnect.", + "data": { + "email": "Indirizzo email", + "password": "Password", + "interval": "Intervallo di aggiornamento (minuti)", + "interval_charging": "Intervallo di aggiornamento durante la ricarica (minuti)", + "interval_statistics": "Intervallo di aggiornamento per le statistiche giornaliere/mensili (minuti)", + "region": "Regione (EU, US)", + "imperial_distance": "Utilizza unità di misura imperiali per la distanza" + } + } + }, + "error": { + "auth_error": "Credenziali non valide fornite.", + "region_error": "Regione non valida fornita. Fornisci una regione valida." + }, + "abort": {} + }, + "options": { + "step": { + "init": { + "description": "Aggiorna le informazioni del tuo account NissanConnect.", + "data": { + "email": "Indirizzo email", + "password": "Password", + "interval": "Intervallo di aggiornamento (minuti)", + "interval_charging": "Intervallo di aggiornamento durante la ricarica (minuti)", + "interval_statistics": "Intervallo di aggiornamento per le statistiche giornaliere/mensili (minuti)", + "imperial_distance": "Utilizza unità di misura imperiali per la distanza" + }, + "data_description": { + "password": "Se non stai cambiando le tue credenziali, lascia vuoto il campo della password." + } + } + }, + "error": { + "auth_error": "Credenziali non valide fornite." + }, + "abort": {} + }, + "issues": {}, + "entity": { + "binary_sensor": { + "charging": { + "name": "In carica" + }, + "doors_locked": { + "name": "Porte chiuse a chiave" + }, + "plugged": { + "name": "Inserito" + } + }, + "button": { + "flash_lights": { + "name": "Lampeggia luci" + }, + "honk_horn": { + "name": "Suona il clacson" + }, + "update_data": { + "name": "Aggiorna dati" + } + }, + "climate": { + "climate": { + "name": "Clima" + } + }, + "device_tracker": { + "location": { + "name": "Posizione" + } + }, + "sensor": { + "battery_level": { + "name": "Livello della batteria" + }, + "internal_temperature": { + "name": "Temperatura interna" + }, + "external_temperature": { + "name": "Temperatura esterna" + }, + "range_ac_on": { + "name": "Autonomia (AC attivato)" + }, + "range_ac_off": { + "name": "Autonomia (AC disattivato)" + }, + "odometer": { + "name": "Contachilometri" + }, + "charge_time_3kw": { + "name": "Tempo di ricarica (3 kW)" + }, + "charge_time_6kw": { + "name": "Tempo di ricarica (6 kW)" + }, + "last_updated": { + "name": "Ultimo aggiornamento" + }, + "daily_distance": { + "name": "Distanza giornaliera" + }, + "daily_efficiency": { + "name": "Efficienza giornaliera" + }, + "daily_trips": { + "name": "Viaggi giornalieri" + }, + "monthly_distance": { + "name": "Distanza mensile" + }, + "monthly_efficiency": { + "name": "Efficienza mensile" + }, + "monthly_trips": { + "name": "Viaggi mensili" + } + } + } +}