Skip to content

Conversation

@Danielhiversen
Copy link
Member

Breaking change

Proposed change

Add Homevolt battery integration

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

Signed-off-by: Daniel Hjelseth Høyer <[email protected]>
Signed-off-by: Daniel Hjelseth Høyer <[email protected]>
Signed-off-by: Daniel Hjelseth Høyer <[email protected]>
Signed-off-by: Daniel Hjelseth Høyer <[email protected]>
Signed-off-by: Daniel Hjelseth Høyer <[email protected]>
Signed-off-by: Daniel Hjelseth Høyer <[email protected]>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new integration for Homevolt battery systems, enabling local network monitoring and control of Homevolt battery devices. The integration implements Bronze-level quality scale requirements with support for config flow-based setup, sensor entities, and a data update coordinator for polling device status.

Key changes:

  • Implements config flow with connection validation and error handling for authentication and connectivity issues
  • Adds sensor platform supporting multiple sensor types (power, energy, voltage, temperature, battery percentage, etc.)
  • Uses DataUpdateCoordinator for efficient 15-second polling updates with proper error handling

Reviewed changes

Copilot reviewed 13 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
homeassistant/components/homevolt/init.py Entry point implementing async setup/unload with coordinator initialization and connection testing
homeassistant/components/homevolt/config_flow.py Config flow with user input validation, unique ID enforcement, and exception handling
homeassistant/components/homevolt/coordinator.py Data update coordinator managing device polling with authentication and connection error handling
homeassistant/components/homevolt/sensor.py Sensor platform creating entities for various device measurements with device info and unique IDs
homeassistant/components/homevolt/const.py Domain constants and type definitions including custom ConfigEntry type
homeassistant/components/homevolt/manifest.json Integration metadata defining requirements, IoT class, and quality scale level
homeassistant/components/homevolt/strings.json Localization strings for config flow UI elements
homeassistant/components/homevolt/quality_scale.yaml Quality scale rule tracking showing Bronze completion and Silver/Gold progress
tests/components/homevolt/test_config_flow.py Comprehensive config flow tests covering success, error recovery, and duplicate prevention
tests/components/homevolt/conftest.py Test fixtures mocking async_setup_entry for config flow tests
requirements_all.txt Adds homevolt==0.2.3 dependency
requirements_test_all.txt Adds homevolt==0.2.3 test dependency
homeassistant/generated/config_flows.py Registers homevolt in config flow list
homeassistant/generated/integrations.json Adds integration metadata to generated registry

Comment on lines +156 to +161
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{device_id}_{sensor_device_id}")},
configuration_url=coordinator.client.hostname,
manufacturer=MANUFACTURER,
model=device_metadata.model,
name=device_metadata.name,
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The device_metadata retrieved using .get() could return None if the sensor_device_id is not found in the device_metadata dictionary. Accessing .model and .name attributes on a None value will raise an AttributeError. Consider adding validation to handle this case, either by checking if device_metadata is not None before accessing its attributes, or by using a default value.

Suggested change
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{device_id}_{sensor_device_id}")},
configuration_url=coordinator.client.hostname,
manufacturer=MANUFACTURER,
model=device_metadata.model,
name=device_metadata.name,
model = device_metadata.model if device_metadata is not None else None
name = device_metadata.name if device_metadata is not None else description.name
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{device_id}_{sensor_device_id}")},
configuration_url=coordinator.client.hostname,
manufacturer=MANUFACTURER,
model=model,
name=name,

Copilot uses AI. Check for mistakes.
HomevoltSensor(
SensorEntityDescription(
key=sensor_name,
name=sensor_name,
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entity is using the raw sensor_name directly as the name instead of using translation keys. According to the coding guidelines, entities with has_entity_name set to True should use translation keys via the _attr_translation_key attribute. This would make the entity names translatable and consistent with Home Assistant best practices. Consider setting _attr_translation_key in the SensorEntityDescription instead of using name=sensor_name.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +21 to +43
async def async_setup_entry(hass: HomeAssistant, entry: HomevoltConfigEntry) -> bool:
"""Set up Homevolt from a config entry."""
host: str = entry.data[CONF_HOST]
password: str | None = entry.data.get(CONF_PASSWORD)

websession = async_get_clientsession(hass)
client = Homevolt(host, password, websession=websession)

try:
await client.update_info()
except HomevoltConnectionError as err:
raise ConfigEntryNotReady(
f"Unable to connect to Homevolt battery: {err}"
) from err

coordinator = HomevoltDataUpdateCoordinator(hass, entry, client)
await coordinator.async_config_entry_first_refresh()

entry.runtime_data = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The async_setup_entry function in the init module lacks test coverage. Tests should verify successful setup, error handling for connection failures (ConfigEntryNotReady), and that the coordinator is properly initialized with first refresh. Similar integrations in this repository include test_init.py files with comprehensive coverage of setup scenarios.

Copilot uses AI. Check for mistakes.
@Danielhiversen Danielhiversen marked this pull request as draft January 7, 2026 12:38
STEP_USER_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Optional(CONF_PASSWORD): str,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we use the password for? When does a device have one?

Comment on lines +40 to +42
parallel-updates:
status: exempt
comment: Coordinator handles updates, no explicit parallel updates needed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still mention it explicitly

Comment on lines +38 to +49
@dataclass
class Description:
"""Sensor metadata description."""

device_class: SensorDeviceClass | None
state_class: SensorStateClass | None
native_unit_of_measurement: str | None


SENSOR_META: dict[SensorType, Description] = {
SensorType.COUNT: Description(
None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don''t you use SensorEntityDescriptions for this?

sensor_device_id = sensor.device_identifier
device_metadata = coordinator.data.device_metadata.get(sensor_device_id)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{device_id}_{sensor_device_id}")},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats a sensor?

Comment on lines +164 to +167
@property
def native_value(self) -> StateType:
"""Return the native value of the sensor."""
return self.coordinator.data.sensors[self.entity_description.key].value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if a sensor doesn't expose everything?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants