core/tests/components/powerwall/test_sensor.py

309 lines
11 KiB
Python

"""The sensor tests for the powerwall platform."""
from datetime import timedelta
from unittest.mock import Mock, patch
import pytest
from tesla_powerwall import MetersAggregatesResponse
from tesla_powerwall.error import MissingAttributeError
from homeassistant.components.powerwall.const import DOMAIN
from homeassistant.components.sensor import ATTR_STATE_CLASS
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_FRIENDLY_NAME,
ATTR_UNIT_OF_MEASUREMENT,
CONF_IP_ADDRESS,
PERCENTAGE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
import homeassistant.util.dt as dt_util
from .mocks import MOCK_GATEWAY_DIN, _mock_powerwall_with_fixtures
from tests.common import MockConfigEntry, async_fire_time_changed
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensors(hass: HomeAssistant, device_registry: dr.DeviceRegistry) -> None:
"""Test creation of the sensors."""
mock_powerwall = await _mock_powerwall_with_fixtures(hass)
config_entry = MockConfigEntry(domain=DOMAIN, data={CONF_IP_ADDRESS: "1.2.3.4"})
config_entry.add_to_hass(hass)
with (
patch(
"homeassistant.components.powerwall.config_flow.Powerwall",
return_value=mock_powerwall,
),
patch(
"homeassistant.components.powerwall.Powerwall", return_value=mock_powerwall
),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
reg_device = device_registry.async_get_device(
identifiers={("powerwall", MOCK_GATEWAY_DIN)},
)
assert reg_device.model == "PowerWall 2 (GW1)"
assert reg_device.sw_version == "1.50.1 c58c2df3"
assert reg_device.manufacturer == "Tesla"
assert reg_device.name == "MySite"
state = hass.states.get("sensor.mysite_load_power")
assert state.state == "1.971"
attributes = state.attributes
assert attributes[ATTR_DEVICE_CLASS] == "power"
assert attributes[ATTR_UNIT_OF_MEASUREMENT] == "kW"
assert attributes[ATTR_STATE_CLASS] == "measurement"
assert attributes[ATTR_FRIENDLY_NAME] == "MySite Load power"
state = hass.states.get("sensor.mysite_load_frequency")
assert state.state == "60"
attributes = state.attributes
assert attributes[ATTR_DEVICE_CLASS] == "frequency"
assert attributes[ATTR_UNIT_OF_MEASUREMENT] == "Hz"
assert attributes[ATTR_STATE_CLASS] == "measurement"
assert attributes[ATTR_FRIENDLY_NAME] == "MySite Load frequency"
state = hass.states.get("sensor.mysite_load_voltage")
assert state.state == "120.7"
attributes = state.attributes
assert attributes[ATTR_DEVICE_CLASS] == "voltage"
assert attributes[ATTR_UNIT_OF_MEASUREMENT] == "V"
assert attributes[ATTR_STATE_CLASS] == "measurement"
assert attributes[ATTR_FRIENDLY_NAME] == "MySite Load voltage"
state = hass.states.get("sensor.mysite_load_current")
assert state.state == "0"
attributes = state.attributes
assert attributes[ATTR_DEVICE_CLASS] == "current"
assert attributes[ATTR_UNIT_OF_MEASUREMENT] == "A"
assert attributes[ATTR_STATE_CLASS] == "measurement"
assert attributes[ATTR_FRIENDLY_NAME] == "MySite Load current"
assert float(hass.states.get("sensor.mysite_load_export").state) == 1056.8
assert float(hass.states.get("sensor.mysite_load_import").state) == 4693.0
state = hass.states.get("sensor.mysite_battery_power")
assert state.state == "-8.55"
assert float(hass.states.get("sensor.mysite_battery_export").state) == 3620.0
assert float(hass.states.get("sensor.mysite_battery_import").state) == 4216.2
state = hass.states.get("sensor.mysite_solar_power")
assert state.state == "10.49"
assert float(hass.states.get("sensor.mysite_solar_export").state) == 9864.2
assert float(hass.states.get("sensor.mysite_solar_import").state) == 28.2
state = hass.states.get("sensor.mysite_charge")
assert state.state == "47"
expected_attributes = {
"unit_of_measurement": PERCENTAGE,
"friendly_name": "MySite Charge",
"device_class": "battery",
}
# Only test for a subset of attributes in case
# HA changes the implementation and a new one appears
for key, value in expected_attributes.items():
assert state.attributes[key] == value
state = hass.states.get("sensor.mysite_backup_reserve")
assert state.state == "15"
expected_attributes = {
"unit_of_measurement": PERCENTAGE,
"friendly_name": "MySite Backup reserve",
"device_class": "battery",
}
# Only test for a subset of attributes in case
# HA changes the implementation and a new one appears
for key, value in expected_attributes.items():
assert state.attributes[key] == value
mock_powerwall.get_meters.return_value = MetersAggregatesResponse.from_dict({})
mock_powerwall.get_backup_reserve_percentage.return_value = None
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=60))
await hass.async_block_till_done()
assert hass.states.get("sensor.mysite_load_power").state == STATE_UNKNOWN
assert hass.states.get("sensor.mysite_load_frequency").state == STATE_UNKNOWN
assert hass.states.get("sensor.mysite_backup_reserve").state == STATE_UNKNOWN
assert (
float(hass.states.get("sensor.mysite_tg0123456789ab_battery_capacity").state)
== 14.715
)
assert (
float(hass.states.get("sensor.mysite_tg0123456789ab_battery_voltage").state)
== 245.7
)
assert (
float(hass.states.get("sensor.mysite_tg0123456789ab_frequency").state) == 50.0
)
assert float(hass.states.get("sensor.mysite_tg0123456789ab_current").state) == 0.3
assert int(hass.states.get("sensor.mysite_tg0123456789ab_power").state) == -100
assert (
float(hass.states.get("sensor.mysite_tg0123456789ab_battery_export").state)
== 2358.235
)
assert (
float(hass.states.get("sensor.mysite_tg0123456789ab_battery_import").state)
== 2693.355
)
assert (
float(hass.states.get("sensor.mysite_tg0123456789ab_battery_remaining").state)
== 14.715
)
assert (
str(hass.states.get("sensor.mysite_tg0123456789ab_grid_state").state)
== "grid_compliant"
)
assert (
float(hass.states.get("sensor.mysite_tg9876543210ba_battery_capacity").state)
== 15.137
)
assert (
float(hass.states.get("sensor.mysite_tg9876543210ba_battery_voltage").state)
== 245.6
)
assert (
float(hass.states.get("sensor.mysite_tg9876543210ba_frequency").state) == 50.0
)
assert float(hass.states.get("sensor.mysite_tg9876543210ba_current").state) == 0.1
assert int(hass.states.get("sensor.mysite_tg9876543210ba_power").state) == -100
assert (
float(hass.states.get("sensor.mysite_tg9876543210ba_battery_export").state)
== 509.907
)
assert (
float(hass.states.get("sensor.mysite_tg9876543210ba_battery_import").state)
== 610.483
)
assert (
float(hass.states.get("sensor.mysite_tg9876543210ba_battery_remaining").state)
== 15.137
)
assert (
str(hass.states.get("sensor.mysite_tg9876543210ba_grid_state").state)
== "grid_compliant"
)
async def test_sensor_backup_reserve_unavailable(hass: HomeAssistant) -> None:
"""Confirm that backup reserve sensor is not added if data is unavailable from the device."""
mock_powerwall = await _mock_powerwall_with_fixtures(hass)
mock_powerwall.get_backup_reserve_percentage.side_effect = MissingAttributeError(
Mock(), "backup_reserve_percent", "operation"
)
config_entry = MockConfigEntry(domain=DOMAIN, data={CONF_IP_ADDRESS: "1.2.3.4"})
config_entry.add_to_hass(hass)
with (
patch(
"homeassistant.components.powerwall.config_flow.Powerwall",
return_value=mock_powerwall,
),
patch(
"homeassistant.components.powerwall.Powerwall", return_value=mock_powerwall
),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get("sensor.powerwall_backup_reserve")
assert state is None
async def test_sensors_with_empty_meters(hass: HomeAssistant) -> None:
"""Test creation of the sensors with empty meters."""
mock_powerwall = await _mock_powerwall_with_fixtures(hass, empty_meters=True)
config_entry = MockConfigEntry(domain=DOMAIN, data={CONF_IP_ADDRESS: "1.2.3.4"})
config_entry.add_to_hass(hass)
with (
patch(
"homeassistant.components.powerwall.config_flow.Powerwall",
return_value=mock_powerwall,
),
patch(
"homeassistant.components.powerwall.Powerwall", return_value=mock_powerwall
),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get("sensor.mysite_solar_power") is None
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_unique_id_migrate(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test we can migrate unique ids of the sensors."""
config_entry = MockConfigEntry(domain=DOMAIN, data={CONF_IP_ADDRESS: "1.2.3.4"})
config_entry.add_to_hass(hass)
mock_powerwall = await _mock_powerwall_with_fixtures(hass)
old_unique_id = "_".join(sorted(["TG0123456789AB", "TG9876543210BA"]))
new_unique_id = MOCK_GATEWAY_DIN
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
identifiers={("powerwall", old_unique_id)},
manufacturer="Tesla",
)
old_mysite_load_power_entity = entity_registry.async_get_or_create(
"sensor",
DOMAIN,
unique_id=f"{old_unique_id}_load_instant_power",
suggested_object_id="mysite_load_power",
config_entry=config_entry,
)
assert old_mysite_load_power_entity.entity_id == "sensor.mysite_load_power"
with (
patch(
"homeassistant.components.powerwall.config_flow.Powerwall",
return_value=mock_powerwall,
),
patch(
"homeassistant.components.powerwall.Powerwall", return_value=mock_powerwall
),
):
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
reg_device = device_registry.async_get_device(
identifiers={("powerwall", MOCK_GATEWAY_DIN)},
)
old_reg_device = device_registry.async_get_device(
identifiers={("powerwall", old_unique_id)},
)
assert old_reg_device is None
assert reg_device is not None
assert (
entity_registry.async_get_entity_id(
"sensor", DOMAIN, f"{old_unique_id}_load_instant_power"
)
is None
)
assert (
entity_registry.async_get_entity_id(
"sensor", DOMAIN, f"{new_unique_id}_load_instant_power"
)
is not None
)
state = hass.states.get("sensor.mysite_load_power")
assert state.state == "1.971"