Fix unique_id of S0 meters connected to Fronius inverters (#61408)

pull/61413/head
Matthias Alphart 2021-12-10 00:35:20 +01:00 committed by GitHub
parent 58174eaa4e
commit 4f9d4872a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 462 additions and 15 deletions

View File

@ -784,15 +784,22 @@ class MeterSensor(_FroniusSensorEntity):
self._entity_id_prefix = f"meter_{solar_net_id}"
super().__init__(coordinator, key, solar_net_id)
meter_data = self._device_data()
# S0 meters connected directly to inverters respond "n.a." as serial number
# `model` contains the inverter id: "S0 Meter at inverter 1"
if (meter_uid := meter_data["serial"]["value"]) == "n.a.":
meter_uid = (
f"{coordinator.solar_net.solar_net_device_id}:"
f'{meter_data["model"]["value"]}'
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, meter_data["serial"]["value"])},
identifiers={(DOMAIN, meter_uid)},
manufacturer=meter_data["manufacturer"]["value"],
model=meter_data["model"]["value"],
name=meter_data["model"]["value"],
via_device=(DOMAIN, coordinator.solar_net.solar_net_device_id),
)
self._attr_unique_id = f'{meter_data["serial"]["value"]}-{key}'
self._attr_unique_id = f"{meter_uid}-{key}"
class OhmpilotSensor(_FroniusSensorEntity):

View File

@ -1,4 +1,6 @@
"""Tests for the Fronius integration."""
from __future__ import annotations
from homeassistant.components.fronius.const import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
@ -10,16 +12,16 @@ from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
MOCK_HOST = "http://fronius"
MOCK_UID = "123.4567890" # has to match mocked logger unique_id
MOCK_UID = "123.4567890"
async def setup_fronius_integration(
hass: HomeAssistant, is_logger: bool = True
hass: HomeAssistant, is_logger: bool = True, unique_id: str = MOCK_UID
) -> ConfigEntry:
"""Create the Fronius integration."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id=MOCK_UID,
unique_id=unique_id, # has to match mocked logger unique_id
data={
CONF_HOST: MOCK_HOST,
"is_logger": is_logger,
@ -35,9 +37,10 @@ def mock_responses(
aioclient_mock: AiohttpClientMocker,
host: str = MOCK_HOST,
fixture_set: str = "symo",
inverter_ids: list[str | int] = [1],
night: bool = False,
) -> None:
"""Mock responses for Fronius Symo inverter with meter."""
"""Mock responses for Fronius devices."""
aioclient_mock.clear_requests()
_night = "_night" if night else ""
@ -45,14 +48,15 @@ def mock_responses(
f"{host}/solar_api/GetAPIVersion.cgi",
text=load_fixture(f"{fixture_set}/GetAPIVersion.json", "fronius"),
)
aioclient_mock.get(
f"{host}/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&"
"DeviceId=1&DataCollection=CommonInverterData",
text=load_fixture(
f"{fixture_set}/GetInverterRealtimeData_Device_1{_night}.json",
"fronius",
),
)
for inverter_id in inverter_ids:
aioclient_mock.get(
f"{host}/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&"
f"DeviceId={inverter_id}&DataCollection=CommonInverterData",
text=load_fixture(
f"{fixture_set}/GetInverterRealtimeData_Device_{inverter_id}{_night}.json",
"fronius",
),
)
aioclient_mock.get(
f"{host}/solar_api/v1/GetInverterInfo.cgi",
text=load_fixture(f"{fixture_set}/GetInverterInfo.json", "fronius"),

View File

@ -0,0 +1,5 @@
{
"APIVersion" : 1,
"BaseURL" : "/solar_api/v1/",
"CompatibilityRange" : "1.6-3"
}

View File

@ -0,0 +1,33 @@
{
"Body" : {
"Data" : {
"1" : {
"CustomName" : "Primo 5.0-1",
"DT" : 76,
"ErrorCode" : 0,
"PVPower" : 5160,
"Show" : 1,
"StatusCode" : 7,
"UniqueID" : "123456"
},
"2" : {
"CustomName" : "Primo 3.0-1",
"DT" : 81,
"ErrorCode" : 0,
"PVPower" : 3240,
"Show" : 1,
"StatusCode" : 7,
"UniqueID" : "234567"
}
}
},
"Head" : {
"RequestArguments" : {},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:34:06-03:00"
}
}

View File

@ -0,0 +1,64 @@
{
"Body" : {
"Data" : {
"DAY_ENERGY" : {
"Unit" : "Wh",
"Value" : 22504
},
"DeviceStatus" : {
"ErrorCode" : 0,
"LEDColor" : 2,
"LEDState" : 0,
"MgmtTimerRemainingTime" : -1,
"StateToReset" : false,
"StatusCode" : 7
},
"FAC" : {
"Unit" : "Hz",
"Value" : 60
},
"IAC" : {
"Unit" : "A",
"Value" : 3.8500000000000001
},
"IDC" : {
"Unit" : "A",
"Value" : 4.2300000000000004
},
"PAC" : {
"Unit" : "W",
"Value" : 862
},
"TOTAL_ENERGY" : {
"Unit" : "Wh",
"Value" : 17114940
},
"UAC" : {
"Unit" : "V",
"Value" : 223.90000000000001
},
"UDC" : {
"Unit" : "V",
"Value" : 452.30000000000001
},
"YEAR_ENERGY" : {
"Unit" : "Wh",
"Value" : 7532755.5
}
}
},
"Head" : {
"RequestArguments" : {
"DataCollection" : "CommonInverterData",
"DeviceClass" : "Inverter",
"DeviceId" : "1",
"Scope" : "Device"
},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:34:08-03:00"
}
}

View File

@ -0,0 +1,64 @@
{
"Body" : {
"Data" : {
"DAY_ENERGY" : {
"Unit" : "Wh",
"Value" : 14237
},
"DeviceStatus" : {
"ErrorCode" : 0,
"LEDColor" : 2,
"LEDState" : 0,
"MgmtTimerRemainingTime" : -1,
"StateToReset" : false,
"StatusCode" : 7
},
"FAC" : {
"Unit" : "Hz",
"Value" : 60.009999999999998
},
"IAC" : {
"Unit" : "A",
"Value" : 1.3200000000000001
},
"IDC" : {
"Unit" : "A",
"Value" : 0.96999999999999997
},
"PAC" : {
"Unit" : "W",
"Value" : 296
},
"TOTAL_ENERGY" : {
"Unit" : "Wh",
"Value" : 5796010
},
"UAC" : {
"Unit" : "V",
"Value" : 223.59999999999999
},
"UDC" : {
"Unit" : "V",
"Value" : 329.5
},
"YEAR_ENERGY" : {
"Unit" : "Wh",
"Value" : 3596193.25
}
}
},
"Head" : {
"RequestArguments" : {
"DataCollection" : "CommonInverterData",
"DeviceClass" : "Inverter",
"DeviceId" : "2",
"Scope" : "Device"
},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:36:15-03:00"
}
}

View File

@ -0,0 +1,29 @@
{
"Body" : {
"LoggerInfo" : {
"CO2Factor" : 0.52999997138977051,
"CO2Unit" : "kg",
"CashCurrency" : "BRL",
"CashFactor" : 1,
"DefaultLanguage" : "en",
"DeliveryFactor" : 1,
"HWVersion" : "2.4E",
"PlatformID" : "wilma",
"ProductID" : "fronius-datamanager-card",
"SWVersion" : "3.18.7-1",
"TimezoneLocation" : "Sao_Paulo",
"TimezoneName" : "-03",
"UTCOffset" : 4294956496,
"UniqueID" : "123.4567890"
}
},
"Head" : {
"RequestArguments" : {},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:34:09-03:00"
}
}

View File

@ -0,0 +1,31 @@
{
"Body" : {
"Data" : {
"0" : {
"Details" : {
"Manufacturer" : "Fronius",
"Model" : "S0 Meter at inverter 1",
"Serial" : "n.a."
},
"Enable" : 1,
"EnergyReal_WAC_Minus_Relative" : 191.25,
"Meter_Location_Current" : 1,
"PowerReal_P_Sum" : -2216.7486858112229,
"TimeStamp" : 1639074843,
"Visible" : 1
}
}
},
"Head" : {
"RequestArguments" : {
"DeviceClass" : "Meter",
"Scope" : "System"
},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:34:04-03:00"
}
}

View File

@ -0,0 +1,17 @@
{
"Body" : {
"Data" : {}
},
"Head" : {
"RequestArguments" : {
"DeviceClass" : "OhmPilot",
"Scope" : "System"
},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:34:05-03:00"
}
}

View File

@ -0,0 +1,45 @@
{
"Body" : {
"Data" : {
"Inverters" : {
"1" : {
"DT" : 76,
"E_Day" : 22502,
"E_Total" : 17114930,
"E_Year" : 7532753.5,
"P" : 886
},
"2" : {
"DT" : 81,
"E_Day" : 14222,
"E_Total" : 5795989.5,
"E_Year" : 3596179.75,
"P" : 948
}
},
"Site" : {
"E_Day" : 36724,
"E_Total" : 22910919.5,
"E_Year" : 11128933.25,
"Meter_Location" : "load",
"Mode" : "vague-meter",
"P_Akku" : null,
"P_Grid" : 384.93491437299008,
"P_Load" : -2218.9349143729901,
"P_PV" : 1834,
"rel_Autonomy" : 82.652266550064084,
"rel_SelfConsumption" : 100
},
"Version" : "12"
}
},
"Head" : {
"RequestArguments" : {},
"Status" : {
"Code" : 0,
"Reason" : "",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:34:06-03:00"
}
}

View File

@ -0,0 +1,14 @@
{
"Body" : {
"Data" : {}
},
"Head" : {
"RequestArguments" : {},
"Status" : {
"Code" : 255,
"Reason" : "GetStorageRealtimeData request is not supported by this device.",
"UserMessage" : ""
},
"Timestamp" : "2021-12-09T15:34:05-03:00"
}
}

View File

@ -1,4 +1,5 @@
"""Tests for the Fronius sensor platform."""
from homeassistant.components.fronius.const import DOMAIN
from homeassistant.components.fronius.coordinator import (
FroniusInverterUpdateCoordinator,
FroniusMeterUpdateCoordinator,
@ -6,6 +7,7 @@ from homeassistant.components.fronius.coordinator import (
)
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import STATE_UNKNOWN
from homeassistant.helpers import device_registry as dr
from homeassistant.util import dt
from . import enable_all_entities, mock_responses, setup_fronius_integration
@ -371,7 +373,9 @@ async def test_gen24_storage(hass, aioclient_mock):
assert state.state == str(expected_state)
mock_responses(aioclient_mock, fixture_set="gen24_storage")
config_entry = await setup_fronius_integration(hass, is_logger=False)
config_entry = await setup_fronius_integration(
hass, is_logger=False, unique_id="12345678"
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 36
await enable_all_entities(
@ -469,3 +473,133 @@ async def test_gen24_storage(hass, aioclient_mock):
assert_state("sensor.temperature_cell_fronius_storage_0_http_fronius", 21.5)
assert_state("sensor.capacity_designed_fronius_storage_0_http_fronius", 16588)
assert_state("sensor.voltage_dc_fronius_storage_0_http_fronius", 0.0)
# Devices
device_registry = dr.async_get(hass)
solar_net = device_registry.async_get_device(
identifiers={(DOMAIN, "solar_net_12345678")}
)
assert solar_net.configuration_url == "http://fronius"
assert solar_net.manufacturer == "Fronius"
assert solar_net.name == "SolarNet"
inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "12345678")})
assert inverter_1.manufacturer == "Fronius"
assert inverter_1.model == "Gen24"
assert inverter_1.name == "Gen24 Storage"
meter = device_registry.async_get_device(identifiers={(DOMAIN, "1234567890")})
assert meter.manufacturer == "Fronius"
assert meter.model == "Smart Meter TS 65A-3"
assert meter.name == "Smart Meter TS 65A-3"
ohmpilot = device_registry.async_get_device(identifiers={(DOMAIN, "23456789")})
assert ohmpilot.manufacturer == "Fronius"
assert ohmpilot.model == "Ohmpilot 6"
assert ohmpilot.name == "Ohmpilot"
assert ohmpilot.sw_version == "1.0.25-3"
storage = device_registry.async_get_device(
identifiers={(DOMAIN, "P030T020Z2001234567 ")}
)
assert storage.manufacturer == "BYD"
assert storage.model == "BYD Battery-Box Premium HV"
assert storage.name == "BYD Battery-Box Premium HV"
async def test_primo_s0(hass, aioclient_mock):
"""Test Fronius Primo dual inverter with S0 meter entities."""
def assert_state(entity_id, expected_state):
state = hass.states.get(entity_id)
assert state
assert state.state == str(expected_state)
mock_responses(aioclient_mock, fixture_set="primo_s0", inverter_ids=[1, 2])
config_entry = await setup_fronius_integration(hass, is_logger=True)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 30
await enable_all_entities(
hass, config_entry.entry_id, FroniusMeterUpdateCoordinator.default_interval
)
assert len(hass.states.async_all(domain_filter=SENSOR_DOMAIN)) == 41
# logger
assert_state("sensor.cash_factor_fronius_logger_info_0_http_fronius", 1)
assert_state("sensor.co2_factor_fronius_logger_info_0_http_fronius", 0.53)
assert_state("sensor.delivery_factor_fronius_logger_info_0_http_fronius", 1)
# inverter 1
assert_state("sensor.energy_total_fronius_inverter_1_http_fronius", 17114940)
assert_state("sensor.energy_day_fronius_inverter_1_http_fronius", 22504)
assert_state("sensor.voltage_dc_fronius_inverter_1_http_fronius", 452.3)
assert_state("sensor.power_ac_fronius_inverter_1_http_fronius", 862)
assert_state("sensor.error_code_fronius_inverter_1_http_fronius", 0)
assert_state("sensor.current_dc_fronius_inverter_1_http_fronius", 4.23)
assert_state("sensor.status_code_fronius_inverter_1_http_fronius", 7)
assert_state("sensor.energy_year_fronius_inverter_1_http_fronius", 7532755.5)
assert_state("sensor.current_ac_fronius_inverter_1_http_fronius", 3.85)
assert_state("sensor.voltage_ac_fronius_inverter_1_http_fronius", 223.9)
assert_state("sensor.frequency_ac_fronius_inverter_1_http_fronius", 60)
assert_state("sensor.led_color_fronius_inverter_1_http_fronius", 2)
assert_state("sensor.led_state_fronius_inverter_1_http_fronius", 0)
# inverter 2
assert_state("sensor.energy_total_fronius_inverter_2_http_fronius", 5796010)
assert_state("sensor.energy_day_fronius_inverter_2_http_fronius", 14237)
assert_state("sensor.voltage_dc_fronius_inverter_2_http_fronius", 329.5)
assert_state("sensor.power_ac_fronius_inverter_2_http_fronius", 296)
assert_state("sensor.error_code_fronius_inverter_2_http_fronius", 0)
assert_state("sensor.current_dc_fronius_inverter_2_http_fronius", 0.97)
assert_state("sensor.status_code_fronius_inverter_2_http_fronius", 7)
assert_state("sensor.energy_year_fronius_inverter_2_http_fronius", 3596193.25)
assert_state("sensor.current_ac_fronius_inverter_2_http_fronius", 1.32)
assert_state("sensor.voltage_ac_fronius_inverter_2_http_fronius", 223.6)
assert_state("sensor.frequency_ac_fronius_inverter_2_http_fronius", 60.01)
assert_state("sensor.led_color_fronius_inverter_2_http_fronius", 2)
assert_state("sensor.led_state_fronius_inverter_2_http_fronius", 0)
# meter
assert_state("sensor.meter_location_fronius_meter_0_http_fronius", 1)
assert_state("sensor.power_real_fronius_meter_0_http_fronius", -2216.7487)
# power_flow
assert_state("sensor.power_load_fronius_power_flow_0_http_fronius", -2218.9349)
assert_state(
"sensor.power_battery_fronius_power_flow_0_http_fronius", STATE_UNKNOWN
)
assert_state("sensor.meter_mode_fronius_power_flow_0_http_fronius", "vague-meter")
assert_state("sensor.power_photovoltaics_fronius_power_flow_0_http_fronius", 1834)
assert_state("sensor.power_grid_fronius_power_flow_0_http_fronius", 384.9349)
assert_state(
"sensor.relative_self_consumption_fronius_power_flow_0_http_fronius", 100
)
assert_state("sensor.relative_autonomy_fronius_power_flow_0_http_fronius", 82.6523)
assert_state("sensor.energy_total_fronius_power_flow_0_http_fronius", 22910919.5)
assert_state("sensor.energy_day_fronius_power_flow_0_http_fronius", 36724)
assert_state("sensor.energy_year_fronius_power_flow_0_http_fronius", 11128933.25)
# Devices
device_registry = dr.async_get(hass)
solar_net = device_registry.async_get_device(
identifiers={(DOMAIN, "solar_net_123.4567890")}
)
assert solar_net.configuration_url == "http://fronius"
assert solar_net.manufacturer == "Fronius"
assert solar_net.model == "fronius-datamanager-card"
assert solar_net.name == "SolarNet"
assert solar_net.sw_version == "3.18.7-1"
inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "123456")})
assert inverter_1.manufacturer == "Fronius"
assert inverter_1.model == "Primo 5.0-1"
assert inverter_1.name == "Primo 5.0-1"
inverter_2 = device_registry.async_get_device(identifiers={(DOMAIN, "234567")})
assert inverter_2.manufacturer == "Fronius"
assert inverter_2.model == "Primo 3.0-1"
assert inverter_2.name == "Primo 3.0-1"
meter = device_registry.async_get_device(
identifiers={(DOMAIN, "solar_net_123.4567890:S0 Meter at inverter 1")}
)
assert meter.manufacturer == "Fronius"
assert meter.model == "S0 Meter at inverter 1"
assert meter.name == "S0 Meter at inverter 1"