Skip to content

Refactor unit of measurement handling in importer.py#340

Open
lanort wants to merge 1 commit into
DarwinsBuddy:mainfrom
lanort:patch-1
Open

Refactor unit of measurement handling in importer.py#340
lanort wants to merge 1 commit into
DarwinsBuddy:mainfrom
lanort:patch-1

Conversation

@lanort

@lanort lanort commented Mar 23, 2026

Copy link
Copy Markdown

Fix KeyError: 'unitOfMeasurement' in importer.py

Problem

The component was experiencing a KeyError: 'unitOfMeasurement' when the API response from WienerNetze did not include the unitOfMeasurement field in the bewegungsdaten response. This occurred at line 162 in custom_components/wnsm/importer.py where the code directly accessed bewegungsdaten['unitOfMeasurement'] without checking if the key exists.

Error Traceback

KeyError: 'unitOfMeasurement'
File "/config/custom_components/wnsm/importer.py", line 162
  bewegungsdaten['unitOfMeasurement']

Root Cause

The translate_dict() utility function (in utils.py) only adds keys to the result dictionary if the source value is not None. When the WienerNetze API doesn't provide the descriptor.einheit field, the unitOfMeasurement key is completely absent from the bewegungsdaten dictionary, causing a KeyError when accessed directly.

Solution

Implemented a robust fix that:

  1. Uses .get() method: Changed from direct dictionary access (bewegungsdaten['unitOfMeasurement']) to safe access using .get() method
  2. Provides intelligent fallback: When the API doesn't provide the unit, falls back to the unit_of_measurement configured in the sensor
  3. Normalizes units: Converts Home Assistant unit formats (e.g., "kWh", "Wh") to the API's expected format ("KWH", "WH")
  4. Adds warning logging: Logs a warning when the fallback is used, helping with debugging and monitoring
  5. Maintains backward compatibility: When the API provides the unit (normal case), the code works exactly as before

Code Changes

File: custom_components/wnsm/importer.py

Before (lines 162-167):

if bewegungsdaten['unitOfMeasurement'] == 'WH':
    factor = 1e-3
elif bewegungsdaten['unitOfMeasurement'] == 'KWH':
    factor = 1.0
else:
    raise NotImplementedError(f'Unit {bewegungsdaten["unitOfMeasurement"]}" is not yet implemented. Please report!')

After (lines 163-186):

# Get unit of measurement from API response, with fallback to configured unit
unit_of_measurement = bewegungsdaten.get('unitOfMeasurement')
if unit_of_measurement is None:
    # Fallback to the unit configured in the sensor
    # Normalize unit: "kWh" -> "KWH", "Wh" -> "WH"
    normalized_unit = self.unit_of_measurement.upper()
    if 'KW' in normalized_unit:
        unit_of_measurement = 'KWH'
    elif 'W' in normalized_unit:
        unit_of_measurement = 'WH'
    else:
        # If we can't determine the unit, default to KWH (most common)
        unit_of_measurement = 'KWH'
    _LOGGER.warning(
        f"API response missing 'unitOfMeasurement' for zaehlpunkt {self.zaehlpunkt}. "
        f"Using fallback unit: {unit_of_measurement}"
    )

if unit_of_measurement == 'WH':
    factor = 1e-3
elif unit_of_measurement == 'KWH':
    factor = 1.0
else:
    raise NotImplementedError(f'Unit {unit_of_measurement}" is not yet implemented. Please report!')

Testing

  • ✅ Python syntax validation passed
  • ✅ Code follows project's existing style conventions
  • ✅ Checked for similar issues elsewhere in the codebase (none found)
  • ✅ The Importer class already has self.unit_of_measurement from the sensor configuration, making the fallback reliable

Benefits

  1. Prevents crashes: No more KeyError when API doesn't provide unitOfMeasurement
  2. Graceful degradation: Uses sensible defaults when API data is incomplete
  3. Better observability: Warning logs help identify when API responses are incomplete
  4. Maintains accuracy: Properly normalizes units to ensure correct energy calculations
  5. Backward compatible: Doesn't change behavior when API works normally

Additional Notes

The fix leverages the existing self.unit_of_measurement attribute which is passed from the sensor during initialization (defaults to UnitOfEnergy.KILO_WATT_HOUR from Home Assistant). This provides a reliable fallback that matches the sensor's configuration.

@DarwinsBuddy

DarwinsBuddy commented Apr 10, 2026

Copy link
Copy Markdown
Owner

Hi @lanort lanort, could you please rebease?

@lanort

lanort commented Apr 15, 2026

Copy link
Copy Markdown
Author

@DarwinsBuddy Should be good now.

@DarwinsBuddy DarwinsBuddy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fear you didn't look at the PR at all, it seems that you commited something that didn't got unmerged.

I.g. please only commit what you have tested yourself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants