Skip to content
Open
37 changes: 3 additions & 34 deletions src/pyatmo/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ async def update_devices(
module_data["home_id"] = home_id
module_data["id"] = module_data["_id"]
module_data["name"] = module_data.get("module_name")
modules_data.append(normalize_weather_attributes(module_data))
modules_data.append(normalize_weather_attributes(device_data))
modules_data.append(module_data)
modules_data.append(device_data)

self.homes[home_id] = Home(
self.auth,
Expand All @@ -247,7 +247,7 @@ async def update_devices(
},
)
await self.homes[home_id].update(
{HOME: {"modules": [normalize_weather_attributes(device_data)]}},
{HOME: {"modules": [device_data]}},
)
else:
LOG.debug("No home %s (%s) found.", home_id, home_id)
Expand All @@ -264,7 +264,6 @@ async def update_devices(
"station_name",
device_data.get("module_name", "Unknown"),
)
device_data = normalize_weather_attributes(device_data)
if device_data["id"] not in self.modules:
self.modules[device_data["id"]] = getattr(
modules,
Expand Down Expand Up @@ -293,33 +292,3 @@ def find_home_of_device(self, device_data: dict[str, Any]) -> str | None:
),
None,
)


ATTRIBUTES_TO_FIX: dict[str, str] = {
"_id": "id",
"firmware": "firmware_revision",
"wifi_status": "wifi_strength",
"rf_status": "rf_strength",
"Temperature": "temperature",
"Humidity": "humidity",
"Pressure": "pressure",
"CO2": "co2",
"AbsolutePressure": "absolute_pressure",
"Noise": "noise",
"Rain": "rain",
"WindStrength": "wind_strength",
"WindAngle": "wind_angle",
"GustStrength": "gust_strength",
"GustAngle": "gust_angle",
}


def normalize_weather_attributes(raw_data: RawData) -> dict[str, Any]:
"""Normalize weather attributes."""
result: dict[str, Any] = {}
for attribute, value in raw_data.items():
if attribute == "dashboard_data":
result.update(**normalize_weather_attributes(value))
else:
result[ATTRIBUTES_TO_FIX.get(attribute, attribute)] = value
return result
56 changes: 52 additions & 4 deletions src/pyatmo/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,52 @@

LOG: logging.Logger = logging.getLogger(__name__)

ATTRIBUTES_TO_FIX: dict[str, str] = {
"firmware": "firmware_revision",
"firmware_revision": "firmware_revision",
"firmware_name": "firmware_name",
"wifi_status": "wifi_strength",
"rf_status": "rf_strength",
"Temperature": "temperature",
"Humidity": "humidity",
"Pressure": "pressure",
"CO2": "co2",
"AbsolutePressure": "absolute_pressure",
"Noise": "noise",
"Rain": "rain",
"WindStrength": "wind_strength",
"WindAngle": "wind_angle",
"GustStrength": "gust_strength",
"GustAngle": "gust_angle",
"wind_gust": "gust_strength",
"wind_gust_angle": "gust_angle",
}


def normalize_weather_attributes(raw_data: RawData) -> RawData:
"""Normalize weather-related attributes recursively."""

if not isinstance(raw_data, dict):
return (
[normalize_weather_attributes(item) for item in raw_data]
if isinstance(raw_data, list)
else raw_data
)
normalized: dict[str, Any] = {}
for key, value in raw_data.items():
if key == "_id":
normalized["_id"] = value
continue
if key == "dashboard_data" and isinstance(value, dict):
normalized |= normalize_weather_attributes(value)
continue
normalized[ATTRIBUTES_TO_FIX.get(key, key)] = normalize_weather_attributes(
value
)
if "_id" in normalized and "id" not in normalized:
normalized["id"] = normalized["_id"]
return normalized


def fix_id(raw_data: list[RawData | str]) -> list[RawData | str]:
"""Fix known errors in station ids like superfluous spaces."""
Expand Down Expand Up @@ -43,20 +89,22 @@ def extract_raw_data(resp: RawData, tag: str) -> RawData:
msg = "No device found, errors in response"
raise NoDeviceError(msg)

body = normalize_weather_attributes(resp["body"])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do you normalise attributes here already and then again in the home module?

Copy link
Author

Choose a reason for hiding this comment

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

Hi! Thanks for reviewing!
As far as I remember, it seems you're right, I made a mistake there ^^

I will remove the second normalization in the home module. I probably made this while testing to make sure it would be normalized in every case, and forgot to remove it. Thanks for noticing! 😄

Do you have others comments on this PR? So I can apply everything in one commit.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if the home module would be more appropriate to do the normalisation. But I need to check out the branch and look into it locally.

Copy link
Author

Choose a reason for hiding this comment

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

Okay so I wait for your feedback on this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

By all means feel free to investigate yourself as I can't promise fast feedback.

Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

Applying normalization to the entire response body at the extraction level is a significant change in scope. Previously, normalize_weather_attributes was only called selectively in specific parts of the codebase. Now it's applied universally to all data flowing through extract_raw_data. While this ensures consistency across all API endpoints, it also means that code throughout the codebase that wasn't previously receiving normalized data will now get it. This could potentially break assumptions in existing code. Consider verifying that all consumers of extract_raw_data can handle the normalized data format, especially the recursive normalization and the _id/id handling changes.

Copilot uses AI. Check for mistakes.

if tag == "homes":
homes: list[dict[str, Any] | str] = fix_id(resp["body"].get(tag))
homes: list[dict[str, Any] | str] = fix_id(body.get(tag))
if not homes:
LOG.debug("Server response (tag: %s): %s", tag, resp)
msg = "No homes found"
raise NoDeviceError(msg)
return {
tag: homes,
"errors": resp["body"].get("errors", []),
"errors": body.get("errors", []),
}

if not (raw_data := fix_id(resp["body"].get(tag))):
if not (raw_data := fix_id(body.get(tag))):
LOG.debug("Server response (tag: %s): %s", tag, resp)
msg = "No device data available"
raise NoDeviceError(msg)

return {tag: raw_data, "errors": resp["body"].get("errors", [])}
return {tag: raw_data, "errors": body.get("errors", [])}
Loading