Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ homeassistant.components.huawei_lte.*
homeassistant.components.humidifier.*
homeassistant.components.husqvarna_automower.*
homeassistant.components.huum.*
homeassistant.components.hvv_departures.*
homeassistant.components.hydrawise.*
homeassistant.components.hyperion.*
homeassistant.components.hypontech.*
Expand Down
2 changes: 2 additions & 0 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion homeassistant/components/alexa_devices/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==13.8.2"]
"requirements": ["aioamazondevices==14.0.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/apprise/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"iot_class": "cloud_push",
"loggers": ["apprise"],
"quality_scale": "legacy",
"requirements": ["apprise==1.9.1"]
"requirements": ["apprise==1.11.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/conversation/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["hassil==3.5.0", "home-assistant-intents==2026.5.5"]
"requirements": ["hassil==3.5.0", "home-assistant-intents==2026.6.1"]
}
37 changes: 37 additions & 0 deletions homeassistant/components/envertech_evt800/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Envertech EVT800 integration."""

from pyenvertechevt800 import EnvertechEVT800

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from homeassistant.core import HomeAssistant

from .const import PLATFORMS
from .coordinator import EnvertechEVT800Coordinator

type EnvertechEVT800ConfigEntry = ConfigEntry[EnvertechEVT800Coordinator]


async def async_setup_entry(
hass: HomeAssistant, entry: EnvertechEVT800ConfigEntry
) -> bool:
"""Set up Envertech EVT800 from a config entry."""
evt800 = EnvertechEVT800(entry.data[CONF_IP_ADDRESS], entry.data[CONF_PORT])
evt800.start()

coordinator = EnvertechEVT800Coordinator(hass, evt800, entry)

await coordinator.async_config_entry_first_refresh()

entry.runtime_data = coordinator

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


async def async_unload_entry(
hass: HomeAssistant, entry: EnvertechEVT800ConfigEntry
) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
60 changes: 60 additions & 0 deletions homeassistant/components/envertech_evt800/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Config flow for the ENVERTECH EVT800 integration."""

from typing import Any

from pyenvertechevt800 import EnvertechEVT800
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, CONF_TYPE
from homeassistant.helpers import config_validation as cv

from .const import DEFAULT_PORT, DOMAIN, TYPE_TCP_SERVER_MODE

SCHEMA_DEVICE = vol.Schema(
{
vol.Required(CONF_IP_ADDRESS): cv.string,
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
)


class EnvertechFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for Envertech EVT800."""

VERSION = 1
MINOR_VERSION = 1

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""First step in config flow."""
errors: dict[str, str] = {}
if user_input is not None:
ip_address = user_input[CONF_IP_ADDRESS]
port = user_input[CONF_PORT]

self._async_abort_entries_match(
{
CONF_IP_ADDRESS: ip_address,
CONF_PORT: port,
}
)
evt800 = EnvertechEVT800(ip_address, port)

can_connect = await evt800.test_connection()

if not can_connect:
errors["base"] = "cannot_connect"

if not errors:
return self.async_create_entry(
title="Envertech EVT800",
data={CONF_TYPE: TYPE_TCP_SERVER_MODE, **user_input},
)

return self.async_show_form(
step_id="user",
data_schema=SCHEMA_DEVICE,
errors=errors,
)
11 changes: 11 additions & 0 deletions homeassistant/components/envertech_evt800/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Constants for the ENVERTECH EVT800 integration."""

from homeassistant.const import Platform

DOMAIN = "envertech_evt800"

PLATFORMS = [Platform.SENSOR]

DEFAULT_PORT = 14889
TYPE_TCP_SERVER_MODE = ["TCP_SERVER"]
DEFAULT_SCAN_INTERVAL = 60
44 changes: 44 additions & 0 deletions homeassistant/components/envertech_evt800/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Coordinator for Envertech EVT800 integration."""

from datetime import timedelta
import logging
from typing import TYPE_CHECKING, Any

import pyenvertechevt800

from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import DEFAULT_SCAN_INTERVAL, DOMAIN

if TYPE_CHECKING:
from . import EnvertechEVT800ConfigEntry

_LOGGER = logging.getLogger(__name__)


class EnvertechEVT800Coordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Data update coordinator for Envertech EVT800."""

config_entry: EnvertechEVT800ConfigEntry

def __init__(
self,
hass: HomeAssistant,
client: pyenvertechevt800.EnvertechEVT800,
config_entry: EnvertechEVT800ConfigEntry,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
logger=_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
config_entry=config_entry,
)
self.client = client
client.set_data_listener(self.async_set_updated_data)

async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from the device."""
return self.client.data
29 changes: 29 additions & 0 deletions homeassistant/components/envertech_evt800/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Envertech EVT800 entity."""

from homeassistant.const import CONF_IP_ADDRESS
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import EnvertechEVT800Coordinator


class EnvertechEVT800Entity(CoordinatorEntity[EnvertechEVT800Coordinator]):
"""Envertech EVT800 entity."""

_attr_has_entity_name = True

def __init__(self, coordinator: EnvertechEVT800Coordinator) -> None:
"""Initialize Envertech EVT800 entity."""
super().__init__(coordinator)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.config_entry.entry_id)},
configuration_url=f"http://{coordinator.config_entry.data[CONF_IP_ADDRESS]}/",
manufacturer="Envertech",
model_id="EVT800",
)

@property
def available(self) -> bool:
"""Return True if entity is available."""
return super().available and self.coordinator.client.online
12 changes: 12 additions & 0 deletions homeassistant/components/envertech_evt800/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"domain": "envertech_evt800",
"name": "ENVERTECH EVT800",
"codeowners": ["@daniel-bergmann-00"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/envertech_evt800",
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["pyenvertechevt800"],
"quality_scale": "bronze",
"requirements": ["pyenvertechevt800==0.2.4"]
}
90 changes: 90 additions & 0 deletions homeassistant/components/envertech_evt800/quality_scale.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
rules:
# Bronze
action-setup:
status: exempt
comment: |
The integration does not provide any additional actions.
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
The integration does not provide any additional actions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: done
comment: |
Entities of this integration does not explicitly subscribe to events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done

# Silver
action-exceptions:
status: exempt
comment: |
The integration does not provide any actions.
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: todo
reauthentication-flow:
status: todo
comment: |
The integration does not have any authentication.
test-coverage: done
# Gold
devices: done
diagnostics: todo
discovery-update-info: todo
discovery: todo
docs-data-update: done
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices:
status: exempt
comment: |
Integration connects to a single device

entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations:
status: exempt
comment: |
The integration does not have any own exceptions.
icon-translations: todo
reconfiguration-flow: todo
repair-issues:
status: exempt
comment: |
The integration does not support repairing issues.
stale-devices:
status: exempt
comment: |
This integration connects to a single device per configuration entry.

# Platinum
async-dependency: todo
inject-websession:
status: exempt
comment: |
No websession is used
strict-typing: todo
Loading
Loading