Skip to content

Commit

Permalink
Add support for V2C Trydan 2.1.7 (home-assistant#117147)
Browse files Browse the repository at this point in the history
* Support for firmware 2.1.7

* add device ID as unique_id

* add device ID as unique_id

* add test device id as unique_id

* backward compatibility

* move outside try

* Sensor return type

Co-authored-by: Joost Lekkerkerker <[email protected]>

* not needed

* make slave error enum state

* fix enum

* Update homeassistant/components/v2c/sensor.py

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/v2c/strings.json

Co-authored-by: Joost Lekkerkerker <[email protected]>

* Update homeassistant/components/v2c/strings.json

Co-authored-by: Joost Lekkerkerker <[email protected]>

* simplify tests

* fix misspellings from upstream library

* add sensor tests

* just enough coverage for enum sensor

* Refactor V2C tests (home-assistant#117264)

* Refactor V2C tests

* fix rebase issues

* ruff

* review

* fix home-assistant#117296

---------

Co-authored-by: Joost Lekkerkerker <[email protected]>
  • Loading branch information
dgomes and joostlek authored May 30, 2024
1 parent 43c69c7 commit 822273a
Show file tree
Hide file tree
Showing 12 changed files with 586 additions and 7 deletions.
1 change: 0 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -1528,7 +1528,6 @@ omit =
homeassistant/components/v2c/coordinator.py
homeassistant/components/v2c/entity.py
homeassistant/components/v2c/number.py
homeassistant/components/v2c/sensor.py
homeassistant/components/v2c/switch.py
homeassistant/components/vallox/__init__.py
homeassistant/components/vallox/coordinator.py
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/v2c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator

if coordinator.data.ID and entry.unique_id != coordinator.data.ID:
hass.config_entries.async_update_entry(entry, unique_id=coordinator.data.ID)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True
Expand Down
7 changes: 6 additions & 1 deletion homeassistant/components/v2c/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,18 @@ async def async_step_user(
)

try:
await evse.get_data()
data = await evse.get_data()

except TrydanError:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
if data.ID:
await self.async_set_unique_id(data.ID)
self._abort_if_unique_id_configured()

return self.async_create_entry(
title=f"EVSE {user_input[CONF_HOST]}", data=user_input
)
Expand Down
6 changes: 6 additions & 0 deletions homeassistant/components/v2c/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
},
"fv_power": {
"default": "mdi:solar-power-variant"
},
"slave_error": {
"default": "mdi:alert"
},
"battery_power": {
"default": "mdi:home-battery"
}
},
"switch": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/v2c/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/v2c",
"iot_class": "local_polling",
"requirements": ["pytrydan==0.6.0"]
"requirements": ["pytrydan==0.6.1"]
}
25 changes: 23 additions & 2 deletions homeassistant/components/v2c/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import logging

from pytrydan import TrydanData
from pytrydan.models.trydan import SlaveCommunicationState

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand All @@ -18,6 +19,7 @@
from homeassistant.const import UnitOfEnergy, UnitOfPower, UnitOfTime
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType

from .const import DOMAIN
from .coordinator import V2CUpdateCoordinator
Expand All @@ -30,9 +32,11 @@
class V2CSensorEntityDescription(SensorEntityDescription):
"""Describes an EVSE Power sensor entity."""

value_fn: Callable[[TrydanData], float]
value_fn: Callable[[TrydanData], StateType]


_SLAVE_ERROR_OPTIONS = [error.name.lower() for error in SlaveCommunicationState]

TRYDAN_SENSORS = (
V2CSensorEntityDescription(
key="charge_power",
Expand Down Expand Up @@ -75,6 +79,23 @@ class V2CSensorEntityDescription(SensorEntityDescription):
device_class=SensorDeviceClass.POWER,
value_fn=lambda evse_data: evse_data.fv_power,
),
V2CSensorEntityDescription(
key="slave_error",
translation_key="slave_error",
value_fn=lambda evse_data: evse_data.slave_error.name.lower(),
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=_SLAVE_ERROR_OPTIONS,
),
V2CSensorEntityDescription(
key="battery_power",
translation_key="battery_power",
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER,
value_fn=lambda evse_data: evse_data.battery_power,
entity_registry_enabled_default=False,
),
)


Expand Down Expand Up @@ -108,6 +129,6 @@ def __init__(
self._attr_unique_id = f"{entry_id}_{description.key}"

@property
def native_value(self) -> float | None:
def native_value(self) -> StateType:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.data)
46 changes: 46 additions & 0 deletions homeassistant/components/v2c/strings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
},
"step": {
"user": {
"data": {
Expand Down Expand Up @@ -47,6 +50,49 @@
},
"fv_power": {
"name": "Photovoltaic power"
},
"battery_power": {
"name": "Battery power"
},
"slave_error": {
"name": "Slave error",
"state": {
"no_error": "No error",
"communication": "Communication",
"reading": "Reading",
"slave": "Slave",
"waiting_wifi": "Waiting for Wi-Fi",
"waiting_communication": "Waiting communication",
"wrong_ip": "Wrong IP",
"slave_not_found": "Slave not found",
"wrong_slave": "Wrong slave",
"no_response": "No response",
"clamp_not_connected": "Clamp not connected",
"illegal_function": "Illegal function",
"illegal_data_address": "Illegal data address",
"illegal_data_value": "Illegal data value",
"server_device_failure": "Server device failure",
"acknowledge": "Acknowledge",
"server_device_busy": "Server device busy",
"negative_acknowledge": "Negative acknowledge",
"memory_parity_error": "Memory parity error",
"gateway_path_unavailable": "Gateway path unavailable",
"gateway_target_no_resp": "Gateway target no response",
"server_rtu_inactive244_timeout": "Server RTU inactive/timeout",
"invalid_server": "Invalid server",
"crc_error": "CRC error",
"fc_mismatch": "FC mismatch",
"server_id_mismatch": "Server id mismatch",
"packet_length_error": "Packet length error",
"parameter_count_error": "Parameter count error",
"parameter_limit_error": "Parameter limit error",
"request_queue_full": "Request queue full",
"illegal_ip_or_port": "Illegal IP or port",
"ip_connection_failed": "IP connection failed",
"tcp_head_mismatch": "TCP head mismatch",
"empty_message": "Empty message",
"undefined_error": "Undefined error"
}
}
},
"switch": {
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2352,7 +2352,7 @@ pytradfri[async]==9.0.1
pytrafikverket==0.3.10

# homeassistant.components.v2c
pytrydan==0.6.0
pytrydan==0.6.1

# homeassistant.components.usb
pyudev==0.24.1
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1831,7 +1831,7 @@ pytradfri[async]==9.0.1
pytrafikverket==0.3.10

# homeassistant.components.v2c
pytrydan==0.6.0
pytrydan==0.6.1

# homeassistant.components.usb
pyudev==0.24.1
Expand Down
1 change: 1 addition & 0 deletions tests/components/v2c/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ def mock_v2c_client() -> Generator[AsyncMock, None, None]:
client = mock_client.return_value
get_data_json = load_json_object_fixture("get_data.json", DOMAIN)
client.get_data.return_value = TrydanData.from_api(get_data_json)
client.firmware_version = get_data_json["FirmwareVersion"]
yield client
Loading

0 comments on commit 822273a

Please sign in to comment.