Add 8 new languages, full i18n, new sensors and fixes#3
Open
ADNPolymerase wants to merge 21 commits into
Open
Conversation
- Replace hardcoded Polish _attr_name with translation_key on the calendar, RTK map camera and RTK position device_tracker entities - Add the missing device_tracker translation section (en/pl/fr) - Convert status, error, mowing_readiness, cloud_connection and maintenance_status to enum sensors that emit canonical state keys, with localized labels in translations/*.json (Polish wording preserved) - Replace remaining hardcoded Polish in the free-text schedule summary and day labels with neutral English (HA cannot translate free text)
- Rename the select to a shorter 'Mowing zone' / 'Zone de tonte' / 'Mähzone' - Localize the dynamically built zone option labels (all zones / Zone N / Zones N, M) from the HA UI language instead of hard-coded Polish; Polish wording preserved, unknown languages fall back to English
- New 'Next schedule' timestamp sensor exposing the next mowing start (drop-in replacement for the MTrab landroid_cloud entity) - The mower's area_mowed is a lifetime total: expose it as a new 'Total area mowed' sensor (state_class total_increasing) - 'Area mowed today' is now a real daily value (delta from a midnight baseline, persisted across restarts via RestoreSensor) - Fix 'Daily progress' / 'Remaining to mow' to use today's area instead of the lifetime total (previously always clamped to 100% / 0%) - Add next_schedule + area_mowed_total names in en/pl/fr/de
- Weekday abbreviations (schedule sensor, calendar, attributes) now follow the HA language: en/de/fr/pl (e.g. German Mo/Di/Mi...), English fallback - Localize the schedule sensor free-text fallbacks (+ edge / no active slots) - Localize the hard-coded Polish calendar event title and description (Mowing / Day / Duration / Edge cutting / Source) per language - Convert the schedule sensor to a class so it can read the UI language
Use schedules[next_schedule_start] from pyworxcloud (the authoritative, upstream-maintained computation, 14-day lookahead) when present, and fall back to deriving it from the weekly slots ourselves. No regression: the fallback covers the case where the library value is missing or unparseable.
Import TrackerEntity and SourceType from homeassistant.components.device_tracker instead of the deprecated .config_entry / .const aliases (removed in HA 2027.6).
- Mention next schedule sensor, today/total mowed area, FR/DE translations - Add a Mowed area section clarifying that figures are covered area (overlapping passes), so they can exceed the lawn size
Set _attr_name = None on the lawn_mower entity instead of translation_key 'mower'. It is the device's primary entity, so with has_entity_name=True its friendly_name now becomes exactly the device name. This also fixes third-party cards like landroid-card, which strip the device name from other entities' friendly_name by matching the primary entity's friendly_name as the device name prefix — previously that prefix included ' Mower'/' Tondeuse'/etc. and never matched, so every entity in info_card / statistics_card / battery_card kept showing the full 'Vision Cloud <name>'. Removed the now-unused lawn_mower.mower.name key from en/pl/fr/de.
Add an Entity naming section clarifying why the lawn_mower entity has no name of its own: readability, and compatibility with cards like landroid-card that strip the device name from other entities' labels.
Same structure and coverage as en/pl/fr/de (222 keys each, including sensor states, calendar/schedule names, zone select). Natural wording, matching the language coverage of the community landroid_cloud integration.
_device_map() returns the same DeviceHandler instances pyworxcloud already holds, and _enrich_device() mutates them in place (product-item area_mowed, firmware info, RTK map). With always_update=False the DataUpdateCoordinator skips notifying listeners whenever the returned data compares equal to the previous data — which is always true here, since the dict contains the same object references before and after, regardless of what changed on them. This silently prevented entities like area_mowed_total (and the daily progress/remaining sensors derived from it) from ever refreshing outside of MQTT push updates, which don't carry the REST-only product-item fields. Confirmed live: pressing the refresh button did not update last_updated at all before this fix, despite the coordinator's REST refresh path running.
_refresh_from_cloud_cache() previously swallowed all exceptions from _enrich_device() via asyncio.gather(return_exceptions=True) with no logging at all, making it impossible to tell whether the REST refresh path (area_mowed, firmware, RTK map) was actually running, failing silently, or genuinely returning stale data from Worx's own API. Now logs a warning per device on failure, and a debug line with the fetched area_mowed value on success, to diagnose why Total area mowed appeared to only ever refresh at integration startup/reload.
area_mowed only refreshes when Worx's REST product-item endpoint reports a new figure, which can lag for hours during active mowing (confirmed: the endpoint call succeeds and Worx genuinely returns the same stale value for hours). Add a new sensor that estimates today's coverage from today's work time (live via MQTT statistics, delta from a local-midnight baseline like the other daily sensors) multiplied by the mower's average mowing efficiency (m2/h). This moves during the day even when Total/Today area mowed are stuck waiting for Worx to recompute the real figure. Also factor out _work_time_total_minutes() (statistics-preferring, REST fallback) instead of duplicating that logic inline in mower_runtime_total. _mowing_efficiency() deliberately keeps using the REST-only work_time to stay paired with the REST-only area_mowed_total from the same snapshot. Translation added to all 10 languages (224 keys each, validated).
Confirmed live: pressing the Refresh button asks the mower to publish a fresh MQTT payload (including work-time statistics), which updates the estimated area/progress sensors, but passive push updates during active mowing do not reliably include those statistics fields. Manually clicking Refresh every time is impractical, so the coordinator now asks every known device to report in automatically every 5 minutes (LIVE_REFRESH_INTERVAL), same effect as the button, with proper subscribe/unsubscribe on setup/shutdown. Also add 'Estimated daily progress': same today's-runtime x average- efficiency estimate as Estimated area mowed today, expressed as a percentage of the known lawn area, so progress is visible during the day even when the REST-based daily progress sensor is stuck. Translation added to all 10 languages (226 keys each, validated).
The Last update timestamp changes on every MQTT push (as often as every ~20 seconds), which made Home Assistant's logbook narrate a 'changed' entry that often for essentially no user value. Replace the generic STANDARD_SENSORS entry with a dedicated WorxLastUpdateSensor that only accepts a new value once per LAST_UPDATE_REPORT_INTERVAL (24h) and holds the previous one otherwise, restoring the accepted value/timestamp across restarts via RestoreSensor. The logbook now only sees one real change per day for this entity, while last_update_age (minutes since last update) is unaffected and still reflects live data.
Confirmed live on a real Vision mower: Worx's work-time statistics (device.statistics / product-item) can stay frozen for 20+ minutes of continuous mowing despite the periodic forced refresh, so basing the estimated area/progress sensors on that figure inherited the same staleness problem they were meant to work around. Replace _WorxDailyRuntimeBase with _WorxDailyMowingTimeBase, which tracks wall-clock time spent in the mowing/starting status ourselves (accumulated seconds + an in-progress streak start time, both persisted via RestoreSensor), independent of whether Worx reports fresh work-time data. Every coordinator update (MQTT push, ~every 20s) recomputes this, so the estimate now genuinely moves during active mowing. Move MOWING_STATUS_IDS/STARTING_STATUS_IDS to helpers.py as the single source of truth, imported by both lawn_mower.py and sensor.py, so the new accumulator always agrees with what the lawn_mower entity shows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds 8 new languages (
fr,de,nl,es,it,sv,no,da) and routes the whole integration through Home Assistant's native i18n. Several strings were hard-coded Polish in the Python code (entity names, sensor states, schedule, calendar, zone options) and bypassed the translation files; those are fixed too. Also adds a few sensors, fixes the daily progress calculation, and improves entity naming for readability.No dependency changes: still
pyworxcloud==6.3.6.Translations
translations/fr.json,de.json,nl.json,es.json,it.json,sv.json,no.json,da.json— identical key structure toen/pl(222 keys each, validated), natural (non-literal) wording.landroid_cloudintegration (de/fr/nl/es/it/sv/no/da, plus more could follow: cs/hu/ro/et/ru).device_tracker.rtk_positionname to every language.Entity names that ignored translations
Three entities used a hard-coded Polish
_attr_name(which overridestranslation_key):"Harmonogram koszenia"translation_key = "schedule""Mapa RTK"translation_key = "rtk_map_camera""Pozycja RTK"translation_key = "rtk_position"Sensor states
status,error,mowing_readiness,cloud_connectionandmaintenance_statusare nowSensorDeviceClass.ENUMsensors emitting canonical keys, with localized labels under each sensor'sstatein every language. The original Polish wording is preserved inpl.json.Free-text localized by UI language
The schedule sensor summary, the calendar event title/description, and the one-time mowing zone select options are free-form (not declarable in
translations/*.json), so they now followhass.config.language, with English fallback for languages without a schedule mapping. Weekday names included.New / fixed sensors
schedules["next_schedule_start"]and falls back to deriving it from the weekly slots.Primary mower entity has no name (readability + third-party card compatibility)
The
lawn_mowerentity is the device's primary entity, so it now has no name of its own (_attr_name = None, the standard HA convention) instead oftranslation_key = "mower". Withhas_entity_name = Trueitsfriendly_namebecomes exactly the device name.This is both a readability improvement (no more redundant "Vision Cloud Mower" / "Vision Cloud Tondeuse") and a compatibility fix: cards such as landroid-card strip the device name from every other entity's label by matching the primary entity's
friendly_nameas the prefix (itsgetEntityName()helper). Previously that prefix included the extra "Mower"/"Tondeuse" word and never matched, so every label ininfo_card/statistics_card/battery_cardkept showing the full "Vision Cloud " instead of just "". Removed the now-unusedlawn_mower.mower.nametranslation key from every language.Misc
device_trackerimports (TrackerEntity/SourceTypefromhomeassistant.components.device_tracker).koszenie→mowing, displayed via translations). Automations/templates that compared the raw Polish text must compare the canonical key instead. This is the standard HA enum approach required for multi-language support.lawn_mowerentity's displayed name changes from"<Device> Mower"to just"<Device>"(entity_id unchanged, nothing else affected).Validation
python -m py_compilepasses on all modified modules.