Skip to content

Releases: AndrewTapp/solaredgeoptimizers

v2.4.14

14 Mar 10:24
dbc2c74

Choose a tag to compare

v2.4.14 Summary of Changes (SolarEdge Optimizers Integration)

This document summarizes the main changes made to the integration, including new features, lifetime energy logic, reliability improvements, code quality, and documentation updates.


1. New site-level sensors (SolarEdge One only)

  • Installation date

    • Entity: sensor.[prefix]installation_date_[site] (or sensor.[prefix]installation_date when site ID is not in entity IDs).
    • Source: Portal layout/information/site/{siteId}installationDate.
    • Cached for 2 hours (SITE_INFO_CACHE_TTL), same as layout.
  • Peak power

    • Entity: sensor.[prefix]peak_power_[site] (or sensor.[prefix]peak_power when site ID is not in entity IDs).
    • Source: Portal layout/information/site/{siteId}peakPower (kW).
    • Same cache as above.

Both values are fetched via get_site_info_cached() in the SolarEdge One API and stored on the site aggregated data; the sensor platform exposes them only when using the One API.


2. Lifetime energy: portal overrides and removal of threshold

Previous behaviour

  • Site lifetime could use a “portal total” when aggregated data was considered unreliable (below RELIABLE_THRESHOLD_KWH).

Current behaviour

  • Site lifetime

    • Uses the portal’s dashboard production (Wh) from
      dashboard/energy/sites/{siteId}?start-date={installation_date}&end-date=...
      when that value is greater than the aggregated site total.
    • Start date for the request is the site installation date from site info.
  • Inverter and string lifetime

    • Use the portal’s layout/energy by-inverter data (Wh) from
      layout/energy/site/{siteId}/by-inverter?start-date={installation_date}&...
      when the portal value is greater than the aggregated inverter or string total.
  • Removed

    • The constant RELIABLE_THRESHOLD_KWH and all logic based on it.
    • Override rule is now simply: if portal value > aggregated value, use portal value (with start date = installation date).

Implementation details

  • const.py

    • Added SITE_INFO_CACHE_TTL (2 h).
    • Added SENSOR_TYPE_INSTALLATION_DATE, SENSOR_TYPE_PEAK_POWER and included them in SENSOR_TYPE_AGGREGATED_SITE.
    • Removed RELIABLE_THRESHOLD_KWH.
  • solaredge_one_api.py

    • get_site_info_cached(): layout/information/site → installationDate, peakPower (kW).
    • get_dashboard_site_production_cached(installation_date): dashboard/energy/sites → summary.production (Wh).
    • get_layout_energy_by_inverter_cached(installation_date): layout/energy by-inverter → per-inverter and per-string energy (Wh).
    • Caches keyed/used by installation date and date range where relevant.
  • api_dual.py

    • Wraps the above three methods; when not using SolarEdge One, they return empty/None.
  • coordinator.py

    • Fetches site info and installation date; uses it for dashboard production and by-inverter calls.
    • Passes site_info and portal_by_inverter into aggregation.
    • Applies portal overrides for site, inverter, and string lifetime when portal value > aggregated (values from portal are in Wh; converted to kWh for entity state).
  • sensor.py

    • New site-level sensor types for Installation date and Peak power (attributes, units, device classes, translation keys).

3. Reliability and resource handling

  • Unload / file descriptors

    • On integration unload, the coordinator is always retrieved, its API client is closed (my_api.close()), and the coordinator is removed from hass.data, even if async_unload_platforms() fails.
    • Ensures sessions and connections are released and avoids file descriptor leaks (especially for the legacy API’s requests.Session pool).
  • SolarEdge One API close()

    • Made explicitly idempotent: if already closed, returns immediately.
  • Dual API close()

    • Closes both One and legacy clients; tracks and logs if any client fails to close while still attempting to close the other.

4. Debug logging

  • Coordinator: site info fetch result (installation_date, peak_power), dashboard production usage (Wh→kWh), by-inverter fetch and inverter count, and when portal overrides are applied for site, inverter, and string. When creating inverter aggregated data, debug log includes max_active_power (kW).
  • SolarEdge One API: cache miss logs for site info, dashboard production, and by-inverter energy; building inverter node logs maxActivePower (kW) when present.
  • All debug output remains guarded with isEnabledFor(logging.DEBUG) so there is no extra cost at info level.

5. Code quality (CodeFactor / Pylint / pycodestyle)

  • Project root

    • .pylintrc: design limits relaxed (e.g. max-args=10, max-module-lines=2000) so coordinator/sensor and HA callback signatures are not flagged.
    • setup.cfg: [pycodestyle] max-line-length = 159 to align with Pylint.
  • Inline disables

    • # pylint: disable=too-many-arguments added where the design requires more than five parameters (e.g. _calculate_aggregated_data, _register_inverter_and_string_devices, _process_single_string, sensor builder functions, api_dual.__init__, config_flow async_show_form).

6. Translations and i18n

  • All translation files (e.g. translations/*.json) include keys for the new site sensors:
    entity.sensor.installation_date.name, entity.sensor.peak_power.name.
  • Documentation (including internationalization.md) updated to list Installation date and Peak power in the entity sensor names and translation structure.

7. Documentation updates

  • README.md

    • New site-level Installation date and Peak power; lifetime energy described as portal override (dashboard production / by-inverter; start date = installation date); code quality section updated to reference .pylintrc and setup.cfg.
  • info.md

    • Same site sensors and lifetime behaviour; RELIABLE_THRESHOLD removed.
  • docs/Wiki-Home.md

    • Overview, device hierarchy, sensor reference, and API section updated for Installation date and Peak power; caching table includes site info (2 h) and layout (2 h for both One and legacy); lifetime energy and portal overrides described; RELIABLE_THRESHOLD removed and replaced with SITE_INFO_CACHE_TTL in constants; file structure no longer references CODEQUALITY.md/pyproject.toml in the integration folder.
  • docs/SolarEdge-One-API-Summary.md

    • Site info, dashboard production, and by-inverter energy; site-level Installation date and Peak power sensors; portal lifetime overrides and installation date as start date.
  • docs/internationalization.md

    • Installation date and Peak power added to the list of sensor entity names and translation keys.

8. New inverter-level sensor: Max active power (SolarEdge One only)

  • Max active power

    • Entity: sensor.[prefix]max_active_power_[site]_[inverter] (or sensor.[prefix]max_active_power_[inverter] when site ID is not in entity IDs).
    • Source: Portal layout logical v2 .../layout/logical/generic/v2/site/{siteId}?include-optimizers=true → per-inverter properties.maxActivePower (watts). Displayed as kW (same as site Peak power).
    • Cached with the layout (2 h); no separate API call.
    • One API only; legacy layout does not provide this field (sensor still created, value unknown until available).
  • Implementation

    • const.py: SENSOR_TYPE_MAX_ACTIVE_POWER, added to SENSOR_TYPE_AGGREGATED_INVERTER.
    • solaredge_one_api.py: _v2_build_inverter_logical_node reads maxActivePower (W) from inverter properties, converts to kW, adds to inverter data dict.
    • solaredgeoptimizers.py: SolarEdgeInverter has maxActivePower (from data.get("maxActivePower")); SolarEdgeAggregatedData has max_active_power.
    • coordinator.py: _create_inverter_aggregated sets inverter_aggregated.max_active_power = getattr(inverter, "maxActivePower", None); debug log includes max_active_power when creating inverter aggregated.
    • sensor.py: New sensor type in aggregated maps (attr, translation key, units kW); value handling same as Peak power (round to 2 decimals).
    • Translations: All locale files updated with entity.sensor.max_active_power.name (e.g. "Max active power", "Max. Wirkleistung").
  • Inactive inverters: Max active power sensor is still created for inactive inverters (fixed inverter property); value may be unknown if layout does not provide it.


9. File descriptor and documentation refresh

  • File descriptor handling

    • api.py: Protocol close() docstring now states "Release resources, close sessions, and release file descriptors".
    • solaredgeoptimizers.py: Module docstring notes that close() closes all tracked sessions to avoid leaking file descriptors on unload/removal.
    • solaredge_one_api.py: Module docstring notes no persistent sessions (context managers), and close() clears tokens.
    • init.py: Docstring states that on unload or removal the API client is closed to release file descriptors.
    • config_flow.py: Cleanup section now says "Closes API (releases file descriptors), then removes entities and devices".
  • Top-of-file / module comments

    • solaredgeoptimizers.py: Data classes (SolarEdgeInverter maxActivePower, SolarEdgeAggregatedData max_active_power), Key Features (close/file descriptors).
    • sensor.py: Aggregated sensors list includes "Inverter-level: Max active power (kW, from portal layout logical v2; One API only)".
    • coordinator.py: Data aggregation includes "Inverter aggregated data includes max_active_power (kW) from layout logical v2 when available (One API)".
      -...
Read more

v2.4.13

13 Mar 11:42
6574d43

Choose a tag to compare

v2.4.13 Summary of changes — 13 March 2025

This document summarizes the documentation and related updates made on 13 March 2025.


Documentation updates

The following files were reviewed and updated so they accurately reflect the current integration behaviour, constants, and file layout.

README.md

  • Layout cache: Corrected the legacy API layout (panels) cache from "1 hour" to 2 hours. Both SolarEdge One and legacy now document a 2 hour cache (PANELS_CACHE_TTL_ONE, PANELS_CACHE_TTL_LEGACY in const.py).
  • Lifetime energy (per-optimizer): Clarified that string totals are derived by summing that string’s optimizer entries (not from API string-level keys that can be site totals), and that site lifetime is the sum of inverters when reliable, or the portal total when aggregated data is unreliable (e.g. below RELIABLE_THRESHOLD_KWH).
  • Code quality: Added a short note that the project uses Pylint and pycodestyle (e.g. via CodeFactor), with links to pyproject.toml and CODEQUALITY.md for tool config and intentional patterns/suppressions.

info.md

  • Updates / lifetime energy: Replaced the previous “optimizer-level reliable / portal total directly” wording with the same behaviour as the README: string totals from optimizer sums; site lifetime from inverters when reliable or from the portal when unreliable (e.g. below RELIABLE_THRESHOLD_KWH).
  • Debug logging: Documented that debug logging also covers unload (including “unload finally closing API sessions”) and API client closure (dual API: close both clients; config flow: API close on entry removal).

docs/Wiki-Home.md

  • Layout cache: The update-behaviour table and the “Caching” subsection now state the layout (panels) cache is 2 hours for both One and legacy (previously legacy was described as 1 h).
  • Configuration: The single-step config form description now includes Use SolarEdge One in the field order: Site ID → Username → Password → Entity ID prefix → Include Site ID in Entity ID → Use SolarEdge One.
  • File structure (Section 14):
    • Single consolidated api.py entry (SolarEdgeAPIProtocol).
    • Added exceptions.py, CODEQUALITY.md, and pyproject.toml to the repo layout.
    • Corrected doc filename to internationalization.md (was Internationalization.md).
    • Manual-install file list now includes exceptions.py.
  • Lifetime energy (Section 12 – Data and units): Documented that string-level lifetime is always derived by summing that string’s optimizer entries in the coordinator (_build_lifetime_energy_lookup), that the legacy API can return a string key that matches a stringId but holds a site- or inverter-level total, and that the integration does not use that key when optimizer data is available, to avoid double-counting and inflated site totals.

docs/SolarEdge-One-API-Summary.md

  • Polling / stale threshold: “legacy used 2 hours” was updated to “legacy uses 2 hours”.
  • Layout cache: Added that site layout (panels) is cached for 2 hours (PANELS_CACHE_TTL_ONE) when using SolarEdge One.

docs/internationalization.md

  • No content changes. Confirmed it already matches the README (four-section translation structure, Use SolarEdge One, supported languages, link to README translations table).

Summary table

File Changes
README.md Layout cache 2 h (both APIs); lifetime energy aggregation wording; code quality note.
info.md Lifetime energy aggregation wording; debug logging (unload, API close).
Wiki-Home.md Layout cache 2 h; config field order (Use SolarEdge One); file structure (api, exceptions, CODEQUALITY, pyproject, internationalization); manual-install list; lifetime aggregation (string sum, no double-count).
SolarEdge-One-API-Summary.md Legacy “uses” 2 h; layout cache 2 h for One.
internationalization.md Verified up to date; no edits.

Constants reflected in the docs

  • Layout cache: PANELS_CACHE_TTL_ONE and PANELS_CACHE_TTL_LEGACY are both 2 hours (const.py).
  • Lifetime energy: String totals = sum of that string’s optimizers (coordinator); site total = sum of inverters when reliable, or portal total when below RELIABLE_THRESHOLD_KWH (100 kWh).
  • Code quality: pyproject.toml and CODEQUALITY.md are the reference for Pylint/pycodestyle and intentional patterns.

v2.4.12

12 Mar 10:57
a34a528

Choose a tag to compare

v2.4.12 - SolarEdge Optimizers – Changes Summary

This document summarizes changes made to the integration, including recent work on constants, status handling, aggregation, debug logging, CodeFactor-related fixes, documentation, and resource cleanup.


1. Constants and configuration

  • Centralized constants in const.py: Cache TTLs (PANELS_CACHE_TTL_ONE, PANELS_CACHE_TTL_LEGACY, LIFETIME_ENERGY_CACHE_TTL, TEMPERATURE_CACHE_TTL), light-check intervals (LIGHT_CHECK_DESIRED_INTERVAL_FRESH, LIGHT_CHECK_DESIRED_INTERVAL_STALE), API URLs, OAuth client ID, locale-dependent measurement keys (MEASUREMENT_KEYS), and API source labels (OBTAINED_FROM_ONE, OBTAINED_FROM_LEGACY).
  • Module docstring in const.py documents each constant and shared helpers.
  • Status helpers in const.py: is_status_active(status), status_display_value(raw), status_icon(display_value) for consistent handling of blank, Active, Inactive, and other statuses across the integration.

2. Status handling

  • Blank (empty) status is treated as active everywhere: aggregation, sorting, display, and icons.
  • Display: Blank → shown as "blank" with active icon; "Active" / "Inactive" in proper case; any other value shown as-is with unknown (help-circle) icon.
  • Icons: check-circle for Active/blank, alert-circle for Inactive, help-circle for unknown.
  • Child counts (Optimizer count, String count, Inverter count) count only active devices (status blank or "Active").
  • Duplicate resolution (suffixes a, b, c…): active (including blank) items are ordered first, then others.

3. Aggregation behaviour

  • Aggregated values (power, current average, voltage average, lifetime energy) at string, inverter, and site level include data from all devices (any status) that have recent measurements. Averages use the count of devices that contributed data; totals sum all contributing devices.
  • Child counts (Optimizer count per string, String count per inverter, Inverter count per site) count only active devices (status blank or "Active") and are always integers.
  • Coordinator uses optimizer_count / string_count / inverter_count for average divisors and active_optimizers / active_strings / active_inverters for child_count sensors.

4. Debug logging

  • Cache messages now include TTL where relevant:
    • SolarEdge One: Panels, lifetime energy, and temperature cache hit/miss logs include TTL (e.g. "Panels cache miss (TTL=%s)", "Using cached panels (age=%s, TTL=%s)").
    • Legacy: Panels and lifetime energy cache messages include TTL and consistent "(legacy)" prefix.
  • Coordinator: _log_update_cycle_debug logs interval_kind ("fresh" or "stale") and splits the long format string across lines so it stays within line-length limits.
  • Temperature (One API): Added cache-miss debug before fetching when the temperature cache is expired.
  • All debug calls remain guarded with _LOGGER.isEnabledFor(logging.DEBUG).

5. CodeFactor-related fixes

  • config_flow.py: Replaced except Exception: with except Exception as e: in the user step and reauth step; log messages now include the exception (e.g. _LOGGER.exception("Unexpected exception: %s", e)).
  • coordinator.py: Long debug format string in _log_update_cycle_debug split across two lines with implicit string concatenation.
  • solaredgeoptimizers.py:
    • requestSystemData: Long URL split into base + params + url.
    • requestItemHistory: Long chartData URL split into base + q + url; long dict comprehension rewritten as a multi-line dict comprehension.
  • Custom exception: SolarEdgeAPIError in exceptions.py is used for API/processing errors instead of generic Exception where appropriate.
  • Legacy client: Replaced assert with explicit ValueError where parameter validation is required; replaced generic raise Exception(...) with raise SolarEdgeAPIError(...).

6. File descriptor and resource cleanup

  • config_flow.py – validate_input: API client is closed in a finally block after credential validation so temporary sessions are released.
  • config_flow.py – async_remove_entry: When the integration is removed, the coordinator is popped from hass.data and the API client is closed (if present) before calling remove_entities_and_devices_for_entry, so file descriptors are released even if unload did not run or did not close the API.
  • init.py: On setup failure (before the coordinator is stored), the temporary API instance is closed in a finally block.
  • api_dual.py: close() closes both One and Legacy clients with explicit logging; exceptions from one client do not prevent closing the other.
  • Legacy client: close() docstring clarifies that Session.close() releases file descriptors and thread-local session is cleared.

7. Documentation updates

  • README.md: Temperature refresh described as using the temperature cache expiry (30 minutes, TEMPERATURE_CACHE_TTL). Inactive devices section updated: aggregation uses all devices (any status) with recent data; child counts use only active devices.
  • info.md: Inactive devices / aggregation paragraph updated to match (aggregation = all devices; child counts = active only).
  • docs/Wiki-Home.md:
    • Aggregation behaviour (Section 9) rewritten: aggregation values from all devices with recent data; child counts from active only; note on inactive devices contributing to aggregates when they have data.
    • Temperature references updated from "15 min" to "30 min" / TEMPERATURE_CACHE_TTL (data flow note, Section 7 table, Section 12 caching).
    • Removal cleanup: async_remove_entry now documented as closing the API client (releasing file descriptors) when the coordinator is still in hass.data, then calling the shared helper. Config flow table row updated similarly.
  • coordinator.py module docstring: Polling interval described as coordinator tick every 5 minutes; minimum interval between full refreshes set to 5 minutes (LIGHT_CHECK_MIN_INTERVAL). Data aggregation summary updated: power/current/voltage/lifetime from all devices with recent data; child counts from active only.

8. Timing and cache constants (reference)

Constant Value Purpose
UPDATE_DELAY 5 min Coordinator tick interval
LIGHT_CHECK_MIN_INTERVAL 5 min Min time between a light-check-triggered full refresh and the next
LIGHT_CHECK_DESIRED_INTERVAL_FRESH 5 min Desired light-check interval when data is fresh
LIGHT_CHECK_DESIRED_INTERVAL_STALE 30 min Desired light-check interval when data is stale or missing
PANELS_CACHE_TTL_ONE 2 h Layout cache (SolarEdge One)
PANELS_CACHE_TTL_LEGACY 1 h Layout cache (legacy API)
LIFETIME_ENERGY_CACHE_TTL 1 h Lifetime energy cache
TEMPERATURE_CACHE_TTL 30 min Optimizer temperatures cache (One API only)

9. File structure (relevant files)

  • const.py – Constants, status helpers, resolve_duplicate_indices, display-name parsers.
  • exceptions.pySolarEdgeAPIError.
  • config_flow.py – Validation (dual API), API close after validation and on entry removal.
  • __init__.py – Setup with API cleanup on failure; remove_entities_and_devices_for_entry.
  • coordinator.py – Adaptive polling, aggregation (all statuses for values, active only for child counts), device registration.
  • solaredgeoptimizers.py – Legacy API; SolarEdgeAPIError; cache TTLs from const.
  • solaredge_one_api.py – One API; cache TTLs and status check from const.
  • api_dual.py – Dual API; OBTAINED_FROM_* from const; robust close().

This summary reflects the state of the integration after the documented changes. For installation and user-facing features, see the main README and Wiki-Home.

What's Changed

New Contributors

Full Changelog: 2.4.11...2.4.12

v2.4.11

07 Mar 17:11
9f7911a

Choose a tag to compare

Code tidy and documentation update.

Add icons for azimuth and tilt.

Full Changelog: 2.4.10...2.4.11

v2.4.10

07 Mar 16:37
b2a3ffc

Choose a tag to compare

Bug fix, error in previous version.

Full Changelog: 2.4.9...2.4.10

v2.4.9

07 Mar 16:29
7d6878d

Choose a tag to compare

General tidy up.

Added icon logic to status sensors.

Full Changelog: 2.4.8...2.4.9

v2.4.8

07 Mar 15:36
a97603c

Choose a tag to compare

v2.4.8 Inactive Devices Feature

As with previous versions you may need to delete and re-add this integtration after updating through HACS.

This document summarizes the inactive device handling feature added to the SolarEdge Optimizers integration.

Overview

When an optimizer, string, or inverter is marked as Inactive in the SolarEdge portal, the integration now:

  1. Excludes certain sensors that are not meaningful for inactive/disconnected devices
  2. Excludes inactive devices from aggregations so totals and averages only reflect active equipment

Sensors Excluded for Inactive Devices

Inactive Optimizers

The following sensors are not created for inactive optimizers:

Sensor Reason for exclusion
Azimuth No meaningful orientation data for inactive panel
Current No live current reading
Optimizer voltage No live voltage reading
Power No live power reading
Temperature No live temperature reading
Tilt No meaningful orientation data for inactive panel
Voltage No live voltage reading

The following sensors are still created for inactive optimizers:

Sensor Reason for inclusion
Lifetime energy Historical data remains valuable
Last measurement Shows when the optimizer was last active
Status Shows the current status ("Inactive")

Inactive Strings and Inverters

The following sensors are not created for inactive strings/inverters:

Sensor Reason for exclusion
Current (average) No meaningful average from inactive device
Power No live power reading
Voltage (average) No meaningful average from inactive device

The following sensors are still created for inactive strings/inverters:

Sensor Reason for inclusion
Lifetime energy Historical data remains valuable
Last measurement Shows when the device was last active
Child count Shows how many child devices exist
Status Shows the current status ("Inactive")

Aggregation Behavior

Before This Change

All devices were included in aggregations regardless of status, which could result in:

  • Stale or zero values from inactive devices skewing averages
  • Misleading totals that included non-producing equipment

After This Change

Only active devices contribute to aggregations:

Aggregation Level What is included
String aggregations Only optimizers with status "Active"
Inverter aggregations Only strings with status "Active"
Site aggregations Only inverters with status "Active"

This ensures that:

  • Current (average) reflects only actively reporting devices
  • Power totals only include producing equipment
  • Voltage (average) reflects only live readings

Implementation Details

Constants Added (const.py)

SENSOR_TYPE_INACTIVE_OPTIMIZER_EXCLUDE = [
    SENSOR_TYPE_AZIMUTH,
    SENSOR_TYPE_CURRENT,
    SENSOR_TYPE_OPT_VOLTAGE,
    SENSOR_TYPE_POWER,
    SENSOR_TYPE_TEMPERATURE,
    SENSOR_TYPE_TILT,
    SENSOR_TYPE_VOLTAGE,
]

SENSOR_TYPE_INACTIVE_AGGREGATED_EXCLUDE = [
    SENSOR_TYPE_CURRENT,
    SENSOR_TYPE_POWER,
    SENSOR_TYPE_VOLTAGE,
]

Sensor Platform Changes (sensor.py)

  • _build_individual_optimizer_sensors(): Checks optimizer status and skips sensors in SENSOR_TYPE_INACTIVE_OPTIMIZER_EXCLUDE for non-active optimizers
  • _build_aggregated_sensors(): Checks string/inverter status and skips sensors in SENSOR_TYPE_INACTIVE_AGGREGATED_EXCLUDE for non-active devices
  • Added logging to track active/inactive device counts and skipped sensors

Coordinator Changes (coordinator.py)

  • _aggregate_optimizers_in_string(): Only includes optimizer data in string aggregations when optimizer_status == "ACTIVE"
  • _process_inverter_strings(): Only includes string data in inverter aggregations when string_is_active
  • _calculate_aggregated_data(): Only includes inverter data in site aggregations when inverter_is_active

Documentation Updated

The following documentation files were updated to reflect this feature:

File Changes
README.md Added new "Inactive Devices" section
info.md Added "Inactive devices" paragraph
docs/Wiki-Home.md Added new section 9 with detailed tables; renumbered subsequent sections; updated table of contents; added new constants to reference
docs/internationalization.md No changes needed (feature doesn't add new translation keys)

User Impact

What Users Will See

  • Fewer sensors for inactive devices (cleaner device pages)
  • More accurate aggregated values at string, inverter, and site levels
  • No change to active device behavior

Migration Notes

  • No action required from users
  • Existing inactive devices will have fewer sensors after updating
  • Entity history for excluded sensors will no longer update (but historical data is preserved in Home Assistant's database)

Related Features

This feature complements existing functionality:

  • Stale data handling: Live values (power, current, voltage) are zeroed when last measurement exceeds threshold (1h for One API, 2h for Legacy API)
  • Duplicate name handling: Active devices sort first when resolving duplicate names
  • Status sensor: Each device has a Status sensor showing "Active" or "Inactive"

Full Changelog: 2.4.7...2.4.8

v2.4.7

06 Mar 10:53
a7e9930

Choose a tag to compare

v2.4.7 New Sensors and Duplicate Name Handling

Make sure to delete and re-add the integration once it's been updated in HACS.

This document summarizes the changes made to add Status, Azimuth, and Tilt sensors, along with automatic duplicate name resolution for inverters, strings, and optimizers.

New Sensors

Per Optimizer

Sensor Unit Description
Status Optimizer status from the API (e.g. "Active", "Inactive"). Displayed in proper case.
Azimuth ° Panel compass direction (0–360°). Converted from radians to degrees, rounded to 2 decimal places. Only available when the API provides module orientation data.
Tilt ° Panel angle from horizontal. Converted from radians to degrees, rounded to 2 decimal places. Only available when the API provides module orientation data.

Per String

Sensor Description
Status String status from the API (e.g. "Active", "Inactive"). Displayed in proper case.

Per Inverter

Sensor Description
Status Inverter status from the API (e.g. "Active", "Inactive"). Displayed in proper case.

Duplicate Handling

When the SolarEdge API returns multiple inverters, strings, or optimizers with the same position (e.g. after hardware replacement where both old and new units appear), the integration now handles duplicates at two levels:

1. Display Name Resolution (API Layer)

The solaredge_one_api.py resolves duplicate display names from the API:

Entity Type Sorting Order Suffix Pattern
Inverters Active first, then by serial number Inverter 1, Inverter 1a, Inverter 1b, ...
Strings Active first, then by position String 1.0, String 1.0a, String 1.0b, ...
Optimizers Active first, then by serial number Optimizer 1.0.1, Optimizer 1.0.1a, Optimizer 1.0.1b, ...

2. Entity ID Resolution (Sensor/Coordinator Layer)

The sensor.py and coordinator.py resolve duplicate entity IDs independently, ensuring all devices are shown even if they share the same logical position:

Entity Type Sorting Order Entity ID Suffix
Inverters Active first, then by serial number ascending _1, _1a, _1b, ...
Strings Active first, then by serial number/ID _1_0, _1_0a, _1_0b, ...
Optimizers Active first, then by serial number ascending _1_0_1, _1_0_1a, _1_0_1b, ...

How It Works

  1. No deduplication: All inverters, strings, and optimizers found in the API are shown
  2. Entities are grouped by their position key (parsed from display name or enumeration index)
  3. Within each group, entities are sorted:
    • Active status items come first (status = "ACTIVE")
    • Within each status group, sorted alphabetically by serial number
  4. The first item keeps the original position number
  5. Subsequent items receive alphabetical suffixes (a, b, c, ...)

Example

If the API returns two inverters both at position "1":

  • Serial ABC123 with status "Active" → Inverter 1 (entity ID: ..._1)
  • Serial DEF456 with status "Inactive" → Inverter 1a (entity ID: ..._1a)

Entity IDs follow the same pattern:

  • sensor.lifetime_energy_2065855_1 (first inverter)
  • sensor.lifetime_energy_2065855_1a (second inverter)

Files Modified

Core Logic

File Changes
const.py Added SENSOR_TYPE_STATUS, SENSOR_TYPE_AZIMUTH, SENSOR_TYPE_TILT constants; updated sensor type lists; added shared make_duplicate_sort_key() and resolve_duplicate_indices() functions for duplicate resolution
solaredge_one_api.py Added _make_duplicate_sort_key() factory function and _resolve_duplicate_names_with_suffix() function for display name resolution; updated _v2_build_inverter_logical_node, _v2_build_string_logical_node, _v2_build_optimizer_logical_node to extract status; added _extract_azimuth_tilt_from_basic() for radian-to-degree conversion
solaredgeoptimizers.py Updated SolarEdgeInverter, SolarEdgeString, SolarlEdgeOptimizer classes to store status; updated SolarEdgeOptimizerData to include azimuth, tilt, status; updated SolarEdgeAggregatedData to include status
coordinator.py Updated _aggregate_optimizers_in_string() to copy status from site structure to optimizer data; updated _create_string_aggregated() and _create_inverter_aggregated() to copy status to aggregated data objects; added AggregationContext namedtuple to reduce parameter count; uses shared resolve_duplicate_indices() from const.py for entity ID duplicate resolution; added debug logging for status assignments and duplicate resolution
sensor.py Added sensor handling for Status, Azimuth, Tilt; uses shared resolve_duplicate_indices() from const.py for entity ID duplicate resolution in _build_optimizer_tasks() and _build_aggregated_sensors(); updated debug logging to include new fields and duplicate resolution

Translations (19 languages)

All translation files updated with new sensor names:

Language Status Azimuth Tilt
English (en) Status Azimuth Tilt
German (de) Status Azimut Neigung
French (fr) Statut Azimut Inclinaison
Spanish (es) Estado Azimut Inclinación
Italian (it) Stato Azimut Inclinazione
Dutch (nl) Status Azimut Hellingshoek
Portuguese (pt) Estado Azimute Inclinação
Polish (pl) Status Azymut Nachylenie
Swedish (sv) Status Azimut Lutning
Czech (cs) Stav Azimut Sklon
Danish (da) Status Azimut Hældning
Greek (el) Κατάσταση Αζιμούθιο Κλίση
Finnish (fi) Tila Atsimuutti Kallistus
Hungarian (hu) Állapot Azimut Dőlésszög
Norwegian (nb) Status Asimut Helning
Russian (ru) Статус Азимут Наклон
Turkish (tr) Durum Azimut Eğim
Japanese (ja) ステータス 方位角 傾斜角
Chinese (zh) 状态 方位角 倾斜角

Documentation

File Changes
README.md Added new sensors to "What Data You Get" section; added "Handling duplicate names" section
info.md Updated overview and added duplicate name handling note
docs/Wiki-Home.md Updated sensor tables and features list
docs/internationalization.md Added new sensor names to translation reference

Debug Logging

New debug log messages added for:

API Layer (solaredge_one_api.py)

  • Optimizer node building: SolarEdge One: Building optimizer node serial=%s name=%s status=%s
  • String node building: SolarEdge One: Building string node id=%s name=%s status=%s
  • Inverter node building: SolarEdge One: Building inverter node serial=%s name=%s status=%s manufacturer=%s model=%s
  • Duplicate name resolution: SolarEdge One: Resolving %d duplicate names for '%s' and SolarEdge One: Resolved names for '%s'
  • Azimuth/Tilt extraction: SolarEdge One: Optimizer %s azimuth=%s° tilt=%s°

Coordinator Layer (coordinator.py)

  • Status assignment: SolarEdge Optimizers: Set optimizer %s status=%s
  • String aggregated: SolarEdge Optimizers: Created string aggregated %s status=%s child_count=%d active=%d
  • Inverter aggregated: SolarEdge Optimizers: Created inverter aggregated %s status=%s child_count=%d active_optimizers=%d
  • Duplicate resolution: SolarEdge Optimizers: Resolving %d duplicate keys for '%s' and SolarEdge Optimizers: Resolved suffixes for '%s'
  • Duplicate counts: SolarEdge Optimizers: Found %d duplicate inverter positions... and SolarEdge Optimizers: Found %d duplicate string positions...

Sensor Layer (sensor.py)

  • Status update: SolarEdge Optimizers sensor: %s status updated to '%s'
  • Aggregated status update: SolarEdge Optimizers sensor: %s aggregated status updated to '%s'
  • Sensor creation: Updated to include status=%s azimuth=%s° tilt=%s°
  • Optimizer tasks: SolarEdge Optimizers sensor: Built %d optimizer tasks from site structure
  • Duplicate resolution: SolarEdge Optimizers sensor: Found %d duplicate optimizer positions, assigned suffixes (a, b, c...)
  • Aggregated sensors: SolarEdge Optimizers sensor: Building aggregated sensors for %d inverters, %d strings

Shared (const.py)

  • Duplicate key resolution: SolarEdge Optimizers: Resolving %d duplicate keys for '%s': [(status, serial), ...]
  • Resolved suffixes: SolarEdge Optimizers: Resolved suffixes for '%s': {idx: suffix, ...}

Entity ID Patterns

Status Sensors

Level Entity ID Pattern
Optimizer sensor.[prefix]status_[site]_[inverter]_[string]_[optimizer]
String sensor.[prefix]status_[site]_[inverter]_[string]
Inverter sensor.[prefix]status_[site]_[inverter]

Azimuth and Tilt Sensors (Optimizer only)

Sensor Entity ID Pattern
Azimuth sensor.[prefix]azimuth_[site]_[inverter]_[string]_[optimizer]
Tilt sensor.[prefix]tilt_[site]_[inverter]_[string]_[optimizer]

Stale Data Handling

  • Status, Azimuth, and Tilt sensors always show the last known value (not zeroed when stale)
  • This matches the behavior of Lifetime energy and Last measurement sensors

Full Changelog: 2.4.6...2.4.7

v2.4.6

28 Feb 10:37
94733bd

Choose a tag to compare

Display-name-based string and optimizer IDs

Brief summary of the change to use the API display name for device names and entity IDs at string and optimizer level.

What changed

Before: String and optimizer device names and entity IDs were always position-based (1-based from enumerate(..., start=1)). For a user whose API returns string display name "1.0" (zero-based), the device could show "String 1.0" (from the API) but the entity ID was sensor.lifetime_energy_1_1 — a mismatch.

After: At string and optimizer level, device names and entity IDs are based on the API display name when it parses:

  • String display name "1.0" → device "String 1.0", entity IDs like sensor.lifetime_energy_1_0
  • Optimizer display name "1.0.1" → device "Optimizer 1.0.1", entity IDs like sensor.lifetime_energy_1_0_1

If the API display name does not parse (wrong format), the integration falls back to position-based indices so behaviour stays correct. Site and inverter level remain position-based.

Code changes

Location Change
const.py Added parse_string_display_name_path(display_name)(inv, str) or None; parse_optimizer_display_name_to_indices(display_name)(inv, str, opt) or None.
coordinator.py String device creation and _create_string_aggregated use parsed display name for device id/name and entity_id_path when parse succeeds; otherwise use position.
sensor.py _build_optimizer_tasks: use parsed optimizer display name for (inv_idx, str_idx, opt_idx) when parse succeeds. _build_aggregated_sensors: use parsed string display name for entity_id_path. _device_info_for_string: device name placeholder uses string.displayName (API value); device identifier uses entity_id_path (now display-name-based when parse succeeds).

Documentation updated

  • README.md – Hierarchy section, entity ID explanation, hardware replacement.
  • info.md – In Home Assistant paragraph, hardware replacement.
  • docs/Wiki-Home.md – Device identity, entity IDs, sensor platform table, API identity, file structure (const.py).
  • docs/Internationalization.md – Device names and display-name placeholder; “not translated” note.
  • docs/SolarEdge-One-API-Summary.md – Inverter information / 403 note.

Result

For users where the API uses zero-based (or other) string/optimizer numbering (e.g. "1.0", "1.0.1"), device names and entity IDs now match: "String 1.0" and sensor.lifetime_energy_1_0, "Optimizer 1.0.1" and sensor.lifetime_energy_1_0_1. Hardware replacement still works: data is keyed by position, so the same sensor continues to show the new unit’s data after a refresh.

Full Changelog: 2.4.5...2.4.6

v2.4.5

27 Feb 16:21
ae58712

Choose a tag to compare

v2.4.5

Amendments for temperature conversions.

General tidying of code and comments.

Full Changelog: 2.4.4...2.4.5