Add manual and remaining watering time to Gardena Bluetooth for Aquaprecise (#167381)

This commit is contained in:
Jonas
2026-04-04 21:13:57 +02:00
committed by GitHub
parent a5a8cca424
commit b5480da68e
4 changed files with 202 additions and 16 deletions
@@ -111,7 +111,45 @@
'state': '45.0',
})
# ---
# name: test_setup[98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time]
# name: test_setup[service_info0-98bd0f14-0b0e-421a-84e5-ddbf75dc6de4-raw0-number.mock_title_manual_watering_time]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock Title Manual watering time',
'max': 86400,
'min': 0.0,
'mode': <NumberMode.BOX: 'box'>,
'step': 60,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_title_manual_watering_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '100.0',
})
# ---
# name: test_setup[service_info0-98bd0f14-0b0e-421a-84e5-ddbf75dc6de4-raw0-number.mock_title_manual_watering_time].1
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock Title Manual watering time',
'max': 86400,
'min': 0.0,
'mode': <NumberMode.BOX: 'box'>,
'step': 60,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_title_manual_watering_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '10.0',
})
# ---
# name: test_setup[service_info1-98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
@@ -130,7 +168,7 @@
'state': '100.0',
})
# ---
# name: test_setup[98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time].1
# name: test_setup[service_info1-98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time].1
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
@@ -149,7 +187,7 @@
'state': '10.0',
})
# ---
# name: test_setup[98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time].2
# name: test_setup[service_info1-98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time].2
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
@@ -168,7 +206,7 @@
'state': 'unknown',
})
# ---
# name: test_setup[98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time].3
# name: test_setup[service_info1-98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw1-number.mock_title_remaining_open_time].3
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
@@ -187,7 +225,7 @@
'state': 'unavailable',
})
# ---
# name: test_setup[98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw2-number.mock_title_open_for]
# name: test_setup[service_info2-98bd0f13-0b0e-421a-84e5-ddbf75dc6de4-raw2-number.mock_title_open_for]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
@@ -206,7 +244,7 @@
'state': 'unknown',
})
# ---
# name: test_setup[98bd0f14-0b0e-421a-84e5-ddbf75dc6de4-raw0-number.mock_title_manual_watering_time]
# name: test_setup[service_info3-98bd0d13-0b0e-421a-84e5-ddbf75dc6de4-raw3-number.mock_title_manual_watering_time]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
@@ -225,7 +263,7 @@
'state': '100.0',
})
# ---
# name: test_setup[98bd0f14-0b0e-421a-84e5-ddbf75dc6de4-raw0-number.mock_title_manual_watering_time].1
# name: test_setup[service_info3-98bd0d13-0b0e-421a-84e5-ddbf75dc6de4-raw3-number.mock_title_manual_watering_time].1
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
@@ -244,3 +282,79 @@
'state': '10.0',
})
# ---
# name: test_setup[service_info4-98bd0d12-0b0e-421a-84e5-ddbf75dc6de4-raw4-number.mock_title_remaining_watering_time]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock Title Remaining watering time',
'max': 86400,
'min': 0.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 60.0,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_title_remaining_watering_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '100.0',
})
# ---
# name: test_setup[service_info4-98bd0d12-0b0e-421a-84e5-ddbf75dc6de4-raw4-number.mock_title_remaining_watering_time].1
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock Title Remaining watering time',
'max': 86400,
'min': 0.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 60.0,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_title_remaining_watering_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '10.0',
})
# ---
# name: test_setup[service_info4-98bd0d12-0b0e-421a-84e5-ddbf75dc6de4-raw4-number.mock_title_remaining_watering_time].2
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock Title Remaining watering time',
'max': 86400,
'min': 0.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 60.0,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_title_remaining_watering_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup[service_info4-98bd0d12-0b0e-421a-84e5-ddbf75dc6de4-raw4-number.mock_title_remaining_watering_time].3
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'duration',
'friendly_name': 'Mock Title Remaining watering time',
'max': 86400,
'min': 0.0,
'mode': <NumberMode.AUTO: 'auto'>,
'step': 60.0,
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
}),
'context': <ANY>,
'entity_id': 'number.mock_title_remaining_watering_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
@@ -4,12 +4,13 @@ from collections.abc import Awaitable, Callable
from typing import Any
from unittest.mock import Mock, call
from gardena_bluetooth.const import Sensor, Valve
from gardena_bluetooth.const import AquaContourWatering, Sensor, Valve
from gardena_bluetooth.exceptions import (
CharacteristicNoAccess,
GardenaBluetoothException,
)
from gardena_bluetooth.parse import Characteristic
from habluetooth import BluetoothServiceInfo
import pytest
from syrupy.assertion import SnapshotAssertion
@@ -21,15 +22,16 @@ from homeassistant.components.number import (
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from . import setup_entry
from . import AQUA_CONTOUR_SERVICE_INFO, WATER_TIMER_SERVICE_INFO, setup_entry
from tests.common import MockConfigEntry
@pytest.mark.parametrize(
("uuid", "raw", "entity_id"),
("service_info", "uuid", "raw", "entity_id"),
[
(
WATER_TIMER_SERVICE_INFO,
Valve.manual_watering_time.uuid,
[
Valve.manual_watering_time.encode(100),
@@ -38,6 +40,7 @@ from tests.common import MockConfigEntry
"number.mock_title_manual_watering_time",
),
(
WATER_TIMER_SERVICE_INFO,
Valve.remaining_open_time.uuid,
[
Valve.remaining_open_time.encode(100),
@@ -48,18 +51,39 @@ from tests.common import MockConfigEntry
"number.mock_title_remaining_open_time",
),
(
WATER_TIMER_SERVICE_INFO,
Valve.remaining_open_time.uuid,
[Valve.remaining_open_time.encode(100)],
"number.mock_title_open_for",
),
(
AQUA_CONTOUR_SERVICE_INFO,
AquaContourWatering.manual_watering_time.uuid,
[
AquaContourWatering.manual_watering_time.encode(100),
AquaContourWatering.manual_watering_time.encode(10),
],
"number.mock_title_manual_watering_time",
),
(
AQUA_CONTOUR_SERVICE_INFO,
AquaContourWatering.remaining_watering_time.uuid,
[
AquaContourWatering.remaining_watering_time.encode(100),
AquaContourWatering.remaining_watering_time.encode(10),
CharacteristicNoAccess("Test for no access"),
GardenaBluetoothException("Test for errors on bluetooth"),
],
"number.mock_title_remaining_watering_time",
),
],
)
async def test_setup(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
mock_entry: MockConfigEntry,
mock_read_char_raw: dict[str, bytes],
scan_step: Callable[[], Awaitable[None]],
service_info: BluetoothServiceInfo,
uuid: str,
raw: list[bytes],
entity_id: str,
@@ -67,7 +91,7 @@ async def test_setup(
"""Test setup creates expected entities."""
mock_read_char_raw[uuid] = raw[0]
await setup_entry(hass, mock_entry, [Platform.NUMBER])
await setup_entry(hass, platforms=[Platform.NUMBER], service_info=service_info)
assert hass.states.get(entity_id) == snapshot
for char_raw in raw[1:]:
@@ -77,27 +101,43 @@ async def test_setup(
@pytest.mark.parametrize(
("char", "value", "expected", "entity_id"),
("service_info", "char", "value", "expected", "entity_id"),
[
(
WATER_TIMER_SERVICE_INFO,
Valve.manual_watering_time,
100,
100,
"number.mock_title_manual_watering_time",
),
(
WATER_TIMER_SERVICE_INFO,
Valve.remaining_open_time,
100,
100 * 60,
"number.mock_title_open_for",
),
(
AQUA_CONTOUR_SERVICE_INFO,
AquaContourWatering.manual_watering_time,
100,
100,
"number.mock_title_manual_watering_time",
),
(
AQUA_CONTOUR_SERVICE_INFO,
AquaContourWatering.remaining_watering_time,
100,
100,
"number.mock_title_remaining_watering_time",
),
],
)
async def test_config(
hass: HomeAssistant,
mock_entry: MockConfigEntry,
mock_read_char_raw: dict[str, bytes],
mock_client: Mock,
service_info: BluetoothServiceInfo,
char: Characteristic,
value: Any,
expected: Any,
@@ -106,7 +146,7 @@ async def test_config(
"""Test setup creates expected entities."""
mock_read_char_raw[char.uuid] = char.encode(value)
await setup_entry(hass, mock_entry, [Platform.NUMBER])
await setup_entry(hass, platforms=[Platform.NUMBER], service_info=service_info)
assert hass.states.get(entity_id)
await hass.services.async_call(