Add new CentriConnect component (#166933)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Greg Haines
2026-05-11 13:23:50 +02:00
committed by GitHub
co-authored by Copilot
parent 284478f620
commit 261ca2dd9a
23 changed files with 1716 additions and 0 deletions
@@ -0,0 +1,12 @@
"""Tests for the CentriConnect/MyPropane API integration."""
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
"""Set up the CentriConnect/MyPropane integration for testing."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
@@ -0,0 +1,77 @@
"""Common fixtures for the CentriConnect/MyPropane API tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from aiocentriconnect import Tank
import pytest
from homeassistant.components.centriconnect.const import DOMAIN
from homeassistant.const import CONF_DEVICE_ID, CONF_PASSWORD, CONF_USERNAME
from .const import TEST_PASSWORD, TEST_TANK_ID, TEST_TANK_NAME, TEST_USERNAME
from tests.common import MockConfigEntry
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return a mock config entry."""
return MockConfigEntry(
domain=DOMAIN,
unique_id=TEST_TANK_ID,
data={
CONF_DEVICE_ID: TEST_TANK_ID,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
title=TEST_TANK_NAME,
)
@pytest.fixture
def mock_centriconnect_client() -> Generator[AsyncMock]:
"""Mock a CentriConnect/MyPropane client."""
with (
patch(
"homeassistant.components.centriconnect.coordinator.CentriConnect",
autospec=True,
) as mock_client,
patch(
"homeassistant.components.centriconnect.config_flow.CentriConnect",
new=mock_client,
),
):
client = mock_client.return_value
client.async_get_tank_data.return_value = Tank(
{
"AlertStatus": "No Alert",
"Altitude": 123.456,
"BatteryVolts": 4.19,
"DeviceID": TEST_TANK_ID,
"DeviceName": TEST_TANK_NAME,
"DeviceTempCelsius": 17.0,
"DeviceTempFahrenheit": 63.0,
"LastPostTimeIso": "2026-02-27 22:00:31.000",
"Latitude": 40.7128,
"Longitude": -74.0060,
"NextPostTimeIso": "2026-02-28 10:00:00.000",
"SignalQualLTE": -107.0,
"SolarVolts": 2.46,
"TankLevel": 75.0,
"TankSize": 1000,
"TankSizeUnit": "Gallons",
"VersionHW": "4.1",
"VersionLTE": "1.1.2",
}
)
yield client
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.centriconnect.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
+7
View File
@@ -0,0 +1,7 @@
"""Constants for the CentriConnect/MyPropane integration tests."""
TEST_TANK_ID = "123a4b5c-678d-9e0f-a123-4b567c8d901e"
TEST_USERNAME = "12345678-9012-3456-7a89-b012345cde6f"
TEST_PASSWORD = "123456"
TEST_TANK_NAME = "My Tank"
@@ -0,0 +1,695 @@
# serializer version: 1
# name: test_all_entities[sensor.my_tank_alert_status-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'options': list([
'no_alert',
'low_level',
'critical_level',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.my_tank_alert_status',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Alert status',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
'original_icon': None,
'original_name': 'Alert status',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.ALERT_STATUS: 'alert_status'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_alert_status',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.my_tank_alert_status-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'enum',
'friendly_name': 'My Tank Alert status',
'options': list([
'no_alert',
'low_level',
'critical_level',
]),
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_alert_status',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'no_alert',
})
# ---
# name: test_all_entities[sensor.my_tank_altitude-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_altitude',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Altitude',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.DISTANCE: 'distance'>,
'original_icon': None,
'original_name': 'Altitude',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.ALTITUDE: 'altitude'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_altitude',
'unit_of_measurement': <UnitOfLength.METERS: 'm'>,
})
# ---
# name: test_all_entities[sensor.my_tank_altitude-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'distance',
'friendly_name': 'My Tank Altitude',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfLength.METERS: 'm'>,
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_altitude',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '123.456',
})
# ---
# name: test_all_entities[sensor.my_tank_battery-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_battery',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Battery',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
'original_icon': None,
'original_name': 'Battery',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.BATTERY_LEVEL: 'battery_level'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_battery_level',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sensor.my_tank_battery-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'battery',
'friendly_name': 'My Tank Battery',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_battery',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '100.0',
})
# ---
# name: test_all_entities[sensor.my_tank_battery_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_battery_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Battery voltage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Battery voltage',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.BATTERY_VOLTAGE: 'battery_voltage'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_battery_voltage',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_all_entities[sensor.my_tank_battery_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'My Tank Battery voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_battery_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '4.19',
})
# ---
# name: test_all_entities[sensor.my_tank_device_temperature-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_device_temperature',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Device temperature',
'options': dict({
'sensor': dict({
'suggested_display_precision': 1,
}),
}),
'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Device temperature',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.DEVICE_TEMPERATURE: 'device_temperature'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_device_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_all_entities[sensor.my_tank_device_temperature-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'My Tank Device temperature',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_device_temperature',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '17.2222222222222',
})
# ---
# name: test_all_entities[sensor.my_tank_lte_signal_level-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_lte_signal_level',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'LTE signal level',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'LTE signal level',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.LTE_SIGNAL_LEVEL: 'lte_signal_level'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_lte_signal_level',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sensor.my_tank_lte_signal_level-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'My Tank LTE signal level',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_lte_signal_level',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '47.1428571428571',
})
# ---
# name: test_all_entities[sensor.my_tank_lte_signal_strength-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_lte_signal_strength',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'LTE signal strength',
'options': dict({
}),
'original_device_class': <SensorDeviceClass.SIGNAL_STRENGTH: 'signal_strength'>,
'original_icon': None,
'original_name': 'LTE signal strength',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.LTE_SIGNAL_STRENGTH: 'lte_signal_strength'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_lte_signal_strength',
'unit_of_measurement': 'dBm',
})
# ---
# name: test_all_entities[sensor.my_tank_lte_signal_strength-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'signal_strength',
'friendly_name': 'My Tank LTE signal strength',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'dBm',
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_lte_signal_strength',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '-107.0',
})
# ---
# name: test_all_entities[sensor.my_tank_solar_level-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_solar_level',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Solar level',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Solar level',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.SOLAR_LEVEL: 'solar_level'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_solar_level',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sensor.my_tank_solar_level-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'My Tank Solar level',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_solar_level',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '94.6153846153846',
})
# ---
# name: test_all_entities[sensor.my_tank_solar_voltage-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.my_tank_solar_voltage',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Solar voltage',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.VOLTAGE: 'voltage'>,
'original_icon': None,
'original_name': 'Solar voltage',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.SOLAR_VOLTAGE: 'solar_voltage'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_solar_voltage',
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
})
# ---
# name: test_all_entities[sensor.my_tank_solar_voltage-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'voltage',
'friendly_name': 'My Tank Solar voltage',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfElectricPotential.VOLT: 'V'>,
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_solar_voltage',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2.46',
})
# ---
# name: test_all_entities[sensor.my_tank_tank_level-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.my_tank_tank_level',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Tank level',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Tank level',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.TANK_LEVEL: 'tank_level'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_tank_level',
'unit_of_measurement': '%',
})
# ---
# name: test_all_entities[sensor.my_tank_tank_level-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'My Tank Tank level',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_tank_level',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '75.0',
})
# ---
# name: test_all_entities[sensor.my_tank_tank_remaining_volume-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.my_tank_tank_remaining_volume',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Tank remaining volume',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLUME_STORAGE: 'volume_storage'>,
'original_icon': None,
'original_name': 'Tank remaining volume',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.TANK_REMAINING_VOLUME: 'tank_remaining_volume'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_tank_remaining_volume',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
})
# ---
# name: test_all_entities[sensor.my_tank_tank_remaining_volume-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volume_storage',
'friendly_name': 'My Tank Tank remaining volume',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_tank_remaining_volume',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2839.058838',
})
# ---
# name: test_all_entities[sensor.my_tank_tank_size-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.my_tank_tank_size',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Tank size',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
'sensor.private': dict({
'suggested_unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
}),
'original_device_class': <SensorDeviceClass.VOLUME_STORAGE: 'volume_storage'>,
'original_icon': None,
'original_name': 'Tank size',
'platform': 'centriconnect',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <CentriConnectSensorType.TANK_SIZE: 'tank_size'>,
'unique_id': '123a4b5c-678d-9e0f-a123-4b567c8d901e_tank_size',
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
})
# ---
# name: test_all_entities[sensor.my_tank_tank_size-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'volume_storage',
'friendly_name': 'My Tank Tank size',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfVolume.LITERS: 'L'>,
}),
'context': <ANY>,
'entity_id': 'sensor.my_tank_tank_size',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '3785.411784',
})
# ---
@@ -0,0 +1,153 @@
"""Test the CentriConnect/MyPropane API config flow."""
from unittest.mock import AsyncMock
from aiocentriconnect.exceptions import (
CentriConnectConnectionError,
CentriConnectConnectionTimeoutError,
CentriConnectDecodeError,
CentriConnectEmptyResponseError,
CentriConnectNotFoundError,
CentriConnectTooManyRequestsError,
)
import pytest
from homeassistant.components.centriconnect.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_DEVICE_ID, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from .const import TEST_PASSWORD, TEST_TANK_ID, TEST_TANK_NAME, TEST_USERNAME
from tests.common import MockConfigEntry
async def test_full_flow(
hass: HomeAssistant,
mock_centriconnect_client: AsyncMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test full flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_DEVICE_ID: TEST_TANK_ID,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == TEST_TANK_NAME
assert result["data"] == {
CONF_DEVICE_ID: TEST_TANK_ID,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
}
assert result["result"].unique_id == TEST_TANK_ID
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize(
("exception", "error"),
[
(CentriConnectNotFoundError, "invalid_auth"),
(CentriConnectDecodeError("Oh no!", "Bad response"), "unknown"),
(CentriConnectConnectionTimeoutError, "cannot_connect"),
(CentriConnectConnectionError, "cannot_connect"),
(CentriConnectTooManyRequestsError, "cannot_connect"),
(CentriConnectEmptyResponseError, "unknown"),
(Exception, "unknown"),
],
)
async def test_form_errors(
hass: HomeAssistant,
mock_centriconnect_client: AsyncMock,
mock_setup_entry: AsyncMock,
exception: Exception,
error: str,
) -> None:
"""Test flow errors."""
mock_centriconnect_client.async_get_tank_data.side_effect = exception
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_DEVICE_ID: TEST_TANK_ID,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error}
# Make sure the config flow tests finish with either an
# FlowResultType.CREATE_ENTRY or FlowResultType.ABORT so
# we can show the config flow is able to recover from an error.
mock_centriconnect_client.async_get_tank_data.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_DEVICE_ID: TEST_TANK_ID,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == TEST_TANK_NAME
assert result["data"] == {
CONF_DEVICE_ID: TEST_TANK_ID,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
}
assert result["result"].unique_id == TEST_TANK_ID
assert len(mock_setup_entry.mock_calls) == 1
async def test_user_flow_already_configured(
hass: HomeAssistant,
mock_centriconnect_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test that duplicate devices are rejected."""
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_DEVICE_ID: TEST_TANK_ID,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@@ -0,0 +1,29 @@
"""Tests for the CentriConnect/MyPropane sensor platform."""
from unittest.mock import AsyncMock, patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_all_entities(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
mock_centriconnect_client: AsyncMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test all entities."""
with patch("homeassistant.components.centriconnect.PLATFORMS", [Platform.SENSOR]):
await setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)