LaCrosse View new endpoint (#137284)

* Switch to new endpoint in LaCrosse View

* Coverage

* Avoid merge conflict

* Switch to UpdateFailed
This commit is contained in:
IceBotYT 2025-02-08 03:14:00 -05:00 committed by GitHub
parent 64886f717d
commit 332a0c5082
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 151 additions and 45 deletions

View File

@ -10,8 +10,8 @@ from lacrosse_view import HTTPError, LaCrosse, Location, LoginError, Sensor
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import SCAN_INTERVAL
@ -26,6 +26,7 @@ class LaCrosseUpdateCoordinator(DataUpdateCoordinator[list[Sensor]]):
name: str
id: str
hass: HomeAssistant
devices: list[Sensor] | None = None
def __init__(
self,
@ -60,24 +61,34 @@ class LaCrosseUpdateCoordinator(DataUpdateCoordinator[list[Sensor]]):
except LoginError as error:
raise ConfigEntryAuthFailed from error
if self.devices is None:
_LOGGER.debug("Getting devices")
try:
self.devices = await self.api.get_devices(
location=Location(id=self.id, name=self.name),
)
except HTTPError as error:
raise UpdateFailed from error
try:
# Fetch last hour of data
sensors = await self.api.get_sensors(
location=Location(id=self.id, name=self.name),
tz=self.hass.config.time_zone,
start=str(now - 3600),
end=str(now),
)
except HTTPError as error:
raise ConfigEntryNotReady from error
for sensor in self.devices:
sensor.data = (
await self.api.get_sensor_status(
sensor=sensor,
tz=self.hass.config.time_zone,
)
)["data"]["current"]
_LOGGER.debug("Got data: %s", sensor.data)
_LOGGER.debug("Got data: %s", sensors)
except HTTPError as error:
raise UpdateFailed from error
# Verify that we have permission to read the sensors
for sensor in sensors:
for sensor in self.devices:
if not sensor.permissions.get("read", False):
raise ConfigEntryAuthFailed(
f"This account does not have permission to read {sensor.name}"
)
return sensors
return self.devices

View File

@ -48,7 +48,7 @@ def get_value(sensor: Sensor, field: str) -> float | int | str | None:
field_data = sensor.data.get(field) if sensor.data is not None else None
if field_data is None:
return None
value = field_data["values"][-1]["s"]
value = field_data["spot"]["value"]
try:
value = float(value)
except ValueError:

View File

@ -15,7 +15,13 @@ TEST_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["Temperature"],
location=Location(id="1", name="Test"),
data={"Temperature": {"values": [{"s": "2"}], "unit": "degrees_celsius"}},
data={
"data": {
"current": {
"Temperature": {"spot": {"value": "2"}, "unit": "degrees_celsius"}
}
}
},
permissions={"read": True},
model="Test",
)
@ -26,7 +32,13 @@ TEST_NO_PERMISSION_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["Temperature"],
location=Location(id="1", name="Test"),
data={"Temperature": {"values": [{"s": "2"}], "unit": "degrees_celsius"}},
data={
"data": {
"current": {
"Temperature": {"spot": {"value": "2"}, "unit": "degrees_celsius"}
}
}
},
permissions={"read": False},
model="Test",
)
@ -37,7 +49,16 @@ TEST_UNSUPPORTED_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["SomeUnsupportedField"],
location=Location(id="1", name="Test"),
data={"SomeUnsupportedField": {"values": [{"s": "2"}], "unit": "degrees_celsius"}},
data={
"data": {
"current": {
"SomeUnsupportedField": {
"spot": {"value": "2"},
"unit": "degrees_celsius",
}
}
}
},
permissions={"read": True},
model="Test",
)
@ -48,7 +69,13 @@ TEST_FLOAT_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["Temperature"],
location=Location(id="1", name="Test"),
data={"Temperature": {"values": [{"s": "2.3"}], "unit": "degrees_celsius"}},
data={
"data": {
"current": {
"Temperature": {"spot": {"value": "2.3"}, "unit": "degrees_celsius"}
}
}
},
permissions={"read": True},
model="Test",
)
@ -59,7 +86,9 @@ TEST_STRING_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["WetDry"],
location=Location(id="1", name="Test"),
data={"WetDry": {"values": [{"s": "dry"}], "unit": "wet_dry"}},
data={
"data": {"current": {"WetDry": {"spot": {"value": "dry"}, "unit": "wet_dry"}}}
},
permissions={"read": True},
model="Test",
)
@ -70,7 +99,13 @@ TEST_ALREADY_FLOAT_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["HeatIndex"],
location=Location(id="1", name="Test"),
data={"HeatIndex": {"values": [{"s": 2.3}], "unit": "degrees_fahrenheit"}},
data={
"data": {
"current": {
"HeatIndex": {"spot": {"value": 2.3}, "unit": "degrees_fahrenheit"}
}
}
},
permissions={"read": True},
model="Test",
)
@ -81,7 +116,13 @@ TEST_ALREADY_INT_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["WindSpeed"],
location=Location(id="1", name="Test"),
data={"WindSpeed": {"values": [{"s": 2}], "unit": "kilometers_per_hour"}},
data={
"data": {
"current": {
"WindSpeed": {"spot": {"value": 2}, "unit": "kilometers_per_hour"}
}
}
},
permissions={"read": True},
model="Test",
)
@ -92,7 +133,7 @@ TEST_NO_FIELD_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["Temperature"],
location=Location(id="1", name="Test"),
data={},
data={"data": {"current": {}}},
permissions={"read": True},
model="Test",
)
@ -103,7 +144,7 @@ TEST_MISSING_FIELD_DATA_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["Temperature"],
location=Location(id="1", name="Test"),
data={"Temperature": None},
data={"data": {"current": {"Temperature": None}}},
permissions={"read": True},
model="Test",
)
@ -114,7 +155,13 @@ TEST_UNITS_OVERRIDE_SENSOR = Sensor(
sensor_id="2",
sensor_field_names=["Temperature"],
location=Location(id="1", name="Test"),
data={"Temperature": {"values": [{"s": "2.1"}], "unit": "degrees_fahrenheit"}},
data={
"data": {
"current": {
"Temperature": {"spot": {"value": "2.1"}, "unit": "degrees_fahrenheit"}
}
}
},
permissions={"read": True},
model="Test",
)

View File

@ -4,7 +4,7 @@
'coordinator_data': list([
dict({
'__type': "<class 'lacrosse_view.Sensor'>",
'repr': "Sensor(name='Test', device_id='1', type='Test', sensor_id='2', sensor_field_names=['Temperature'], location=Location(id='1', name='Test'), permissions={'read': True}, model='Test', data={'Temperature': {'values': [{'s': '2'}], 'unit': 'degrees_celsius'}})",
'repr': "Sensor(name='Test', device_id='1', type='Test', sensor_id='2', sensor_field_names=['Temperature'], location=Location(id='1', name='Test'), permissions={'read': True}, model='Test', data={'Temperature': {'spot': {'value': '2'}, 'unit': 'degrees_celsius'}})",
}),
]),
'entry': dict({

View File

@ -26,9 +26,14 @@ async def test_entry_diagnostics(
)
config_entry.add_to_hass(hass)
sensor = TEST_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch("lacrosse_view.LaCrosse.get_sensors", return_value=[TEST_SENSOR]),
patch("lacrosse_view.LaCrosse.get_devices", return_value=[sensor]),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

View File

@ -20,12 +20,17 @@ async def test_unload_entry(hass: HomeAssistant) -> None:
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch(
"lacrosse_view.LaCrosse.get_sensors",
return_value=[TEST_SENSOR],
"lacrosse_view.LaCrosse.get_devices",
return_value=[sensor],
),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -68,7 +73,7 @@ async def test_http_error(hass: HomeAssistant) -> None:
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch("lacrosse_view.LaCrosse.get_sensors", side_effect=HTTPError),
patch("lacrosse_view.LaCrosse.get_devices", side_effect=HTTPError),
):
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -84,12 +89,17 @@ async def test_new_token(hass: HomeAssistant, freezer: FrozenDateTimeFactory) ->
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True) as login,
patch(
"lacrosse_view.LaCrosse.get_sensors",
return_value=[TEST_SENSOR],
"lacrosse_view.LaCrosse.get_devices",
return_value=[sensor],
),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -103,7 +113,7 @@ async def test_new_token(hass: HomeAssistant, freezer: FrozenDateTimeFactory) ->
with (
patch("lacrosse_view.LaCrosse.login", return_value=True) as login,
patch(
"lacrosse_view.LaCrosse.get_sensors",
"lacrosse_view.LaCrosse.get_devices",
return_value=[TEST_SENSOR],
),
):
@ -121,12 +131,17 @@ async def test_failed_token(
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True) as login,
patch(
"lacrosse_view.LaCrosse.get_sensors",
return_value=[TEST_SENSOR],
"lacrosse_view.LaCrosse.get_devices",
return_value=[sensor],
),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

View File

@ -32,9 +32,14 @@ async def test_entities_added(hass: HomeAssistant) -> None:
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch("lacrosse_view.LaCrosse.get_sensors", return_value=[TEST_SENSOR]),
patch("lacrosse_view.LaCrosse.get_devices", return_value=[sensor]),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -54,12 +59,17 @@ async def test_sensor_permission(
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_NO_PERMISSION_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch(
"lacrosse_view.LaCrosse.get_sensors",
return_value=[TEST_NO_PERMISSION_SENSOR],
"lacrosse_view.LaCrosse.get_devices",
return_value=[sensor],
),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert not await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -79,11 +89,14 @@ async def test_field_not_supported(
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_UNSUPPORTED_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch(
"lacrosse_view.LaCrosse.get_sensors", return_value=[TEST_UNSUPPORTED_SENSOR]
),
patch("lacrosse_view.LaCrosse.get_devices", return_value=[sensor]),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -114,12 +127,17 @@ async def test_field_types(
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = test_input.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch(
"lacrosse_view.LaCrosse.get_sensors",
"lacrosse_view.LaCrosse.get_devices",
return_value=[test_input],
),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -137,12 +155,17 @@ async def test_no_field(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) -
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_NO_FIELD_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch(
"lacrosse_view.LaCrosse.get_sensors",
return_value=[TEST_NO_FIELD_SENSOR],
"lacrosse_view.LaCrosse.get_devices",
return_value=[sensor],
),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@ -160,12 +183,17 @@ async def test_field_data_missing(hass: HomeAssistant) -> None:
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_ENTRY_DATA)
config_entry.add_to_hass(hass)
sensor = TEST_MISSING_FIELD_DATA_SENSOR.model_copy()
status = sensor.data
sensor.data = None
with (
patch("lacrosse_view.LaCrosse.login", return_value=True),
patch(
"lacrosse_view.LaCrosse.get_sensors",
return_value=[TEST_MISSING_FIELD_DATA_SENSOR],
"lacrosse_view.LaCrosse.get_devices",
return_value=[sensor],
),
patch("lacrosse_view.LaCrosse.get_sensor_status", return_value=status),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()