Skip to content

Conversation

Copy link

Copilot AI commented Sep 16, 2025

This PR migrates and modernizes the API from PPw096/wittiot, adding a comprehensive async HTTP client with structured data models that preserve the native response format optimized for Home Assistant integration.

What's Changed

New API Client

  • EcoWittApi: Modern async HTTP client using aiohttp
  • Native response structure: Preserves original API grouping (common_list, piezoRain, wh25, ch_lds, etc.)
  • Unit parsing with enums: Smart extraction of units from values with comprehensive unit type enums
  • Device ID integration: Maps sensor configuration to live data using actual device IDs
  • Comprehensive error handling: Custom exception classes with proper logging

Data Models

The new API returns data structured exactly as the gateway provides, with enhanced parsing:

from aioecowitt import EcoWittApi

async def main():
    api = EcoWittApi("192.168.1.100")
    data = await api.get_all_data()
    
    # Native grouped structure preserved
    for sensor in data.common_list:
        if sensor.id == "0x02":  # Outdoor temperature
            print(f"Temperature: {sensor.val} {sensor.unit}")  # "20.7" "C"
    
    # Indoor sensor with dedicated unit fields
    if data.wh25 and data.wh25[0].intemp:
        print(f"Indoor: {data.wh25[0].intemp} {data.wh25[0].unit}")
    
    # Device-specific sensors with IDs
    for lds in data.ch_lds:
        if lds.device_id and lds.device_id != "FFFFFFFF":  # Only configured
            print(f"LDS {lds.device_id}: {lds.air} {lds.unit}")
    
    # Sensor configuration with device mapping
    for sensor in data.sensors:
        if sensor.id != "FFFFFFFF":  # Skip unconfigured
            print(f"{sensor.name} (ID: {sensor.id}) - Battery: {sensor.batt}")

Key Features

  • Native API structure: Maintains original response grouping without flattening
  • Unit parsing and enums: Comprehensive unit type system (TemperatureUnit, PressureUnit, SpeedUnit, etc.)
  • Smart value parsing: Extracts numeric values and units from strings like "20.7 C", "58%", "1.6 m/s"
  • Device ID mapping: Uses actual sensor IDs instead of generic channel numbers
  • Configured device filtering: Automatically skips unconfigured devices (ID "FFFFFFFF")
  • Type-safe models: Mashumaro dataclasses with proper string/numeric/enum types
  • Sensor configuration integration: Merges live data with device configuration info

Unit System

Comprehensive enum-based unit system for different measurement types:

  • TemperatureUnit: C, F
  • PressureUnit: hPa, inHg, mmHg, kPa
  • DistanceUnit: mm, in, ft, m
  • SpeedUnit: m/s, mph, km/h, knots, ft/s
  • RainRateUnit: mm/Hr, in/Hr
  • PowerUnit: W/m2, Kfc, Klux
  • PercentageUnit: %
  • VoltageUnit: V

Data Structure Groups

Data organized exactly as the gateway provides:

  • common_list: General sensors with ID-based lookup and parsed units
  • piezoRain: Rain sensor data with battery/voltage diagnostics
  • wh25: Indoor temperature/humidity/pressure with dedicated unit fields
  • ch_lds: Distance sensors with device ID mapping and configuration merge
  • ch_pm25, ch_leak, ch_temp_humi: Channel-based sensors by type
  • sensors: Complete sensor configuration with device IDs and diagnostics
  • debug: Gateway system information

Migration from PPw096/wittiot

This modernizes the original API while preserving its natural structure:

  • Async HTTP requests using aiohttp with proper session management
  • Native response structure preserved instead of artificial flattening
  • Smart unit parsing with enum types instead of raw strings
  • Device ID integration instead of generic channel mapping
  • Configured device filtering to exclude unconfigured sensors
  • Type-safe models with mashumaro serialization support

Home Assistant Integration Benefits

  • Preserved grouping: Perfect for creating logical device entities
  • Unit information: Separate value and unit fields for HA's conversion system
  • Device identification: Real device IDs for unique entity creation
  • Battery monitoring: Complete diagnostics with RSSI, signal strength
  • Configuration awareness: Only exposes actually configured sensors

Breaking Changes

This is a new API client that supplements the existing EcoWitt protocol listener. The original EcoWittListener remains unchanged for backward compatibility.

Testing

  • Core functionality verified with comprehensive test suite
  • Native structure preservation and unit parsing tested
  • Device ID mapping and filtering validated
  • Mashumaro serialization and enum handling verified
  • Example usage code provided for integration guidance

This implementation provides the exact structure needed for seamless Home Assistant integration with properly parsed units, device identification, and comprehensive sensor support that respects the gateway's native data organization.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@edenhaus edenhaus changed the title [WIP] Create a PR where you migrate the API from @PPw096/wittiot/files/wittiot/api.py and modernize the code by using async, aiohttp, and mashumaro. Also, the response should be grouped by devices so it's easier to create a device in Home assistant [WIP] Add pulled based API Sep 16, 2025
Copilot AI changed the title [WIP] Add pulled based API Add modernized async API client with device-grouped data structure Sep 16, 2025
Copilot AI requested a review from edenhaus September 16, 2025 12:24
Copy link
Member

@edenhaus edenhaus left a comment

Choose a reason for hiding this comment

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

@copilot fix requested changes

@edenhaus edenhaus changed the title Add modernized async API client with device-grouped data structure [WIP] Add pulled based API Sep 16, 2025
…versions, fix session handling

Co-authored-by: edenhaus <[email protected]>
Copy link
Author

Copilot AI commented Sep 16, 2025

@copilot fix requested changes

All requested changes have been addressed in commit abd21fe:

  • Reverted Python version to >=3.13
  • Fixed session handling to always use passed session
  • Removed unit conversion functions
  • Implemented mashumaro with proper float/int types
  • Maintained device-grouped data structure

Copilot AI changed the title [WIP] Add pulled based API Add modernized async API client with device-grouped data structure Sep 16, 2025
Copilot AI requested a review from edenhaus September 16, 2025 12:47
Copy link
Member

@edenhaus edenhaus left a comment

Choose a reason for hiding this comment

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

@copilot Implement requested changes

Comment on lines 151 to 154
cleaned = (value.replace("%", "").replace("°", "").replace("V", "")
.replace("hPa", "").replace("inHg", "").replace("mmHg", "")
.replace("mm", "").replace("in", "").replace("mph", "")
.replace("m/s", "").replace("km/h", "").replace("W/m2", ""))
Copy link
Member

Choose a reason for hiding this comment

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

We cannot remove them completely, we need to add them as a separate field to data as HA needs to know in which unit of measurement the data is provided

Copy link
Author

Choose a reason for hiding this comment

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

Added unit parsing back with comprehensive enum support. The API now parses values like "20.7 C" into separate value and unit fields so Home Assistant knows the measurement units. Implemented dedicated enums for different measurement types (Temperature, Pressure, Distance, Speed, etc.) in commit 1acba58.


# Parse LDS sensors
if "ch_lds" in live_data:
for item in live_data["ch_lds"]:
Copy link
Member

Choose a reason for hiding this comment

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

We are still flattening the data, which we should not do it....
Please refactor the code to keep them grouped....

An example response from the gateway is:

{
	"common_list": [
		{
			"id": "0x02",
			"val": "20.7",
			"unit": "C"
		},
		{
			"id": "0x07",
			"val": "58%"
		},
		{
			"id": "3",
			"val": "20.7",
			"unit": "C"
		},
		{
			"id": "5",
			"val": "1.03 kPa"
		},
		{
			"id": "0x03",
			"val": "12.2",
			"unit": "C"
		},
		{
			"id": "0x0B",
			"val": "1.6 m/s"
		},
		{
			"id": "0x0C",
			"val": "1.7 m/s"
		},
		{
			"id": "0x19",
			"val": "3.8 m/s"
		},
		{
			"id": "0x15",
			"val": "450.04 W/m2"
		},
		{
			"id": "0x17",
			"val": "4"
		},
		{
			"id": "0x0A",
			"val": "209"
		},
		{
			"id": "0x6D",
			"val": "254"
		}
	],
	"piezoRain": [
		{
			"id": "srain_piezo",
			"val": "0"
		},
		{
			"id": "0x0D",
			"val": "0.0 mm"
		},
		{
			"id": "0x0E",
			"val": "0.0 mm/Hr"
		},
		{
			"id": "0x7C",
			"val": "0.0 mm"
		},
		{
			"id": "0x10",
			"val": "0.0 mm"
		},
		{
			"id": "0x11",
			"val": "0.0 mm"
		},
		{
			"id": "0x12",
			"val": "57.2 mm"
		},
		{
			"id": "0x13",
			"val": "835.5 mm",
			"battery": "1",
			"voltage": "2.28",
			"ws90cap_volt": "5.4",
			"ws90_ver": "133"
		}
	],
	"wh25": [
		{
			"intemp": "26.9",
			"unit": "C",
			"inhumi": "44%",
			"abs": "895.0 hPa",
			"rel": "895.0 hPa"
		}
	],
	"ch_lds": [
		{
			"channel": "1",
			"name": "",
			"unit": "mm",
			"battery": "5",
			"voltage": "3.28",
			"air": "1565 mm",
			"depth": "--.-",
			"total_height": "0",
			"total_heat": "15"
		}
	],
	"debug": [
		{
			"heap": "97980",
			"runtime": "630104",
			"usr_interval": "30",
			"is_cnip": false
		}
	]
}

As you can see, some properties have a dedicated unit field, and some have the unit as a suffix. We should convert so that all have the unit as a separate field. For the unit, we should use an enum for it. A separate enum per type (e.g. one for temperature, one for wind spee,d and so on)

Also ch_lds is already grouped and we should not flatten it but use this group for our data structure too...

Requesting /get_sensors_info?page=1 currently there exists two pages

Example response looks like:

[{
	"img":	"wh85",
	"type":	"49",
	"name":	"Wind & Rain",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh90",
	"type":	"48",
	"name":	"Temp & Humidity & Solar & Wind & Rain",
	"version":	"133",
	"id":	"4AD4",
	"batt":	"1",
	"rssi":	"-65",
	"signal":	"4",
	"idst":	"1"
},{
	"img":	"wh69",
	"type":	"0",
	"name":	"Temp & Humidity & Solar & Wind & Rain",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh68",
	"type":	"1",
	"name":	"Solar & Wind",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh40",
	"type":	"3",
	"name":	"Rain",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wn20",
	"type":	"70",
	"name":	"Rain Mini",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh25",
	"type":	"4",
	"name":	"Temp & Humidity & Pressure",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh26",
	"type":	"5",
	"name":	"Temp & Humidity",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh80",
	"type":	"2",
	"name":	"Temp & Humidity & Solar & Wind",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh57",
	"type":	"26",
	"name":	"Lightning",
	"id":	"13CE9",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh45",
	"type":	"39",
	"name":	"PM25 & PM10 & CO2",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh41",
	"type":	"22",
	"name":	"PM2.5 CH1",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh41",
	"type":	"23",
	"name":	"PM2.5 CH2",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh41",
	"type":	"24",
	"name":	"PM2.5 CH3",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh41",
	"type":	"25",
	"name":	"PM2.5 CH4",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh55",
	"type":	"27",
	"name":	"Leak CH1",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh55",
	"type":	"28",
	"name":	"Leak CH2",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh55",
	"type":	"29",
	"name":	"Leak CH3",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh55",
	"type":	"30",
	"name":	"Leak CH4",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"6",
	"name":	"Temp & Humidity CH1",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"7",
	"name":	"Temp & Humidity CH2",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"8",
	"name":	"Temp & Humidity CH3",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"9",
	"name":	"Temp & Humidity CH4",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"10",
	"name":	"Temp & Humidity CH5",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"11",
	"name":	"Temp & Humidity CH6",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"12",
	"name":	"Temp & Humidity CH7",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh31",
	"type":	"13",
	"name":	"Temp & Humidity CH8",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh54",
	"type":	"66",
	"name":	"Lds CH1",
	"id":	"2B77",
	"batt":	"5",
	"rssi":	"-62",
	"signal":	"4",
	"idst":	"1"
},{
	"img":	"wh54",
	"type":	"67",
	"name":	"Lds CH2",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh54",
	"type":	"68",
	"name":	"Lds CH3",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh54",
	"type":	"69",
	"name":	"Lds CH4",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
},{
	"img":	"wh51",
	"type":	"14",
	"name":	"Soil moisture CH1",
	"id":	"FFFFFFFF",
	"batt":	"9",
	"rssi":	"--",
	"signal":	"--",
	"idst":	"1"
}]

With this information we know that only one lds devices is currently configured and we can merge this data with readings. So instead of using channel 1 we could use the device id, which should be unique

id = FFFFFFFF means not setup/configured and therefore should be skipped

Copy link
Author

Choose a reason for hiding this comment

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

Completely restructured the API to keep data grouped as requested in commit 1acba58. The API now:

  • Maintains original grouped structure (wh25, common_list, ch_lds, etc.) instead of flattening
  • Parses values and units separately using _parse_value_and_unit() method
  • Uses actual device IDs from sensor configuration instead of generic channel numbers
  • Filters out unconfigured devices (ID "FFFFFFFF") automatically
  • Maps LDS sensor channels to real device IDs from /get_sensors_info

Example: LDS sensor now uses device ID "2B77" instead of "CH1", and includes both the live reading data and sensor configuration info.

Copy link
Author

Copilot AI commented Sep 16, 2025

@copilot Implement requested changes

All requested changes implemented in commit 1acba58:

  • Unit Parsing: Added back with proper enums for different measurement types
  • Grouped Data Structure: Maintains original API response grouping instead of flattening
  • Device ID Mapping: LDS and other sensors now use actual device IDs from sensor configuration
  • Value/Unit Separation: Smart parsing handles "20.7 C", "58%", "1013.2 hPa", "0.0 mm/Hr" formats
  • Unconfigured Device Filtering: Automatically skips devices with ID "FFFFFFFF"

The API now provides the exact structure and unit handling requested for optimal Home Assistant integration.

Copilot AI changed the title Add modernized async API client with device-grouped data structure Add modernized async API client with native response structure and unit parsing Sep 16, 2025
Copilot AI requested a review from edenhaus September 16, 2025 13:26
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.

2 participants