Skip to content

Commit 10c871a

Browse files
authored
Merge pull request #4 from civ0/improve_value_parsing
Improve parsing of float values returned by API
2 parents f59deb8 + 7bed645 commit 10c871a

File tree

4 files changed

+63
-7
lines changed

4 files changed

+63
-7
lines changed

custom_components/eta/api.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ def __init__(self, session, host, port):
77
self._host = host
88
self._port = port
99

10+
self._float_sensor_units = [
11+
"%",
12+
"A",
13+
"Hz",
14+
"Ohm",
15+
"Pa",
16+
"U/min",
17+
"V",
18+
"W",
19+
"W/m²",
20+
"bar",
21+
"kW",
22+
"kWh",
23+
"kg",
24+
"l",
25+
"l/min",
26+
"mV",
27+
"m²",
28+
"s",
29+
"°C",
30+
]
31+
1032
def build_uri(self, suffix):
1133
return "http://" + self._host + ":" + str(self._port) + suffix
1234

@@ -30,11 +52,24 @@ async def does_endpoint_exists(self):
3052
resp = await self.get_request("/user/menu")
3153
return resp.status == 200
3254

55+
async def _parse_data(self, data):
56+
unit = data["@unit"]
57+
if unit in self._float_sensor_units:
58+
scale_factor = int(data["@scaleFactor"])
59+
decimal_places = int(data["@decPlaces"])
60+
raw_value = float(data["#text"])
61+
value = raw_value / scale_factor
62+
value = round(value, decimal_places)
63+
else:
64+
# use default text string representation for values that cannot be parsed properly
65+
value = data["@strValue"]
66+
return value, unit
67+
3368
async def get_data(self, uri):
3469
data = await self.get_request("/user/var/" + str(uri))
3570
text = await data.text()
36-
data = xmltodict.parse(text)
37-
return data["eta"]["value"]["@strValue"], data["eta"]["value"]["@unit"]
71+
data = xmltodict.parse(text)["eta"]["value"]
72+
return await self._parse_data(data)
3873

3974
async def get_raw_sensor_dict(self):
4075
data = await self.get_request("/user/menu/")
@@ -54,8 +89,9 @@ async def get_float_sensors(self):
5489
float_dict = {}
5590
for key in sensor_dict:
5691
try:
57-
raw_value, unit = await self.get_data(sensor_dict[key])
58-
value = float(raw_value)
92+
value, unit = await self.get_data(sensor_dict[key])
93+
if unit not in self._float_sensor_units:
94+
continue
5995
cleaned_key = key.lower().replace(" ", "_")
6096
float_dict[cleaned_key] = (sensor_dict[key], value, unit)
6197
except:

custom_components/eta/sensor.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,16 @@ def __init__(
7878
"""
7979
_LOGGER.warning("ETA Integration - init sensor")
8080

81-
self._attr_state_class = state_class
8281
self._attr_device_class = self.determine_device_class(unit)
82+
8383
if unit == "":
8484
unit = None
85+
86+
if self._attr_device_class == SensorDeviceClass.ENERGY:
87+
self._attr_state_class = SensorStateClass.TOTAL_INCREASING
88+
else:
89+
self._attr_state_class = state_class
90+
8591
self._attr_native_unit_of_measurement = unit
8692
self._attr_native_value = float
8793
id = name.lower().replace(" ", "_")
@@ -107,10 +113,20 @@ async def async_update(self):
107113

108114
@staticmethod
109115
def determine_device_class(unit):
110-
# TODO: Expand this function with more useable units
111116
unit_dict_eta = {
112117
"°C": SensorDeviceClass.TEMPERATURE,
113118
"W": SensorDeviceClass.POWER,
119+
"A": SensorDeviceClass.CURRENT,
120+
"Hz": SensorDeviceClass.FREQUENCY,
121+
"Pa": SensorDeviceClass.PRESSURE,
122+
"V": SensorDeviceClass.VOLTAGE,
123+
"W/m²": SensorDeviceClass.IRRADIANCE,
124+
"bar": SensorDeviceClass.PRESSURE,
125+
"kW": SensorDeviceClass.POWER,
126+
"kWh": SensorDeviceClass.ENERGY,
127+
"kg": SensorDeviceClass.WEIGHT,
128+
"mV": SensorDeviceClass.VOLTAGE,
129+
"s": SensorDeviceClass.DURATION,
114130
}
115131

116132
if unit in unit_dict_eta:

tests/test_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async def test_get_data(monkeypatch):
6666
eta = EtaAPI("session", "host", "port")
6767

6868
value, unit = await eta.get_data("blub")
69-
assert value == "6539"
69+
assert value == 6539
7070
assert unit == "kg"
7171

7272

tests/test_config_flow.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def throw_error_test_url_fixture():
4747
# we want the config flow validation to succeed during the test.
4848

4949

50+
@pytest.mark.asyncio
5051
async def test_successful_config_flow(hass, bypass_test_url_fixture):
5152
"""Test a successful config flow."""
5253
# Initialize a config flow
@@ -84,6 +85,7 @@ async def test_successful_config_flow(hass, bypass_test_url_fixture):
8485

8586

8687
# In this case, we want to simulate a failure during the config flow.
88+
@pytest.mark.asyncio
8789
async def test_failed_config_flow(hass, throw_error_test_url_fixture):
8890
"""Test a failed config flow due to credential validation failure."""
8991
result = await hass.config_entries.flow.async_init(
@@ -101,6 +103,7 @@ async def test_failed_config_flow(hass, throw_error_test_url_fixture):
101103
assert result["errors"] == {"base": "url_broken"}
102104

103105

106+
@pytest.mark.asyncio
104107
async def test_options_flow_remove_sensor(bypass_test_url_fixture, hass):
105108
"""Test config flow options."""
106109
m_instance = AsyncMock()
@@ -137,6 +140,7 @@ async def test_options_flow_remove_sensor(bypass_test_url_fixture, hass):
137140

138141

139142

143+
@pytest.mark.asyncio
140144
async def test_options_flow_add_repo(bypass_test_url_fixture, hass):
141145
"""Test config flow options."""
142146
m_instance = AsyncMock()

0 commit comments

Comments
 (0)