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
2 changes: 1 addition & 1 deletion homeassistant/components/daikin/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/daikin",
"iot_class": "local_polling",
"loggers": ["pydaikin"],
"requirements": ["pydaikin==2.16.0"],
"requirements": ["pydaikin==2.17.1"],
"zeroconf": ["_dkapi._tcp.local."]
}
2 changes: 1 addition & 1 deletion homeassistant/components/google_assistant_sdk/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
},
"media_player": {
"name": "Media player entity",
"description": "Name(s) of media player entities to play response on."
"description": "Name(s) of media player entities to play the Google Assistant's audio response on. This does not target the device for the command itself."
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/google_drive/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from homeassistant.helpers import config_entry_oauth2_flow

_UPLOAD_AND_DOWNLOAD_TIMEOUT = 12 * 3600
_UPLOAD_MAX_RETRIES = 20

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -150,6 +151,7 @@ async def async_upload_backup(
backup_metadata,
open_stream,
backup.size,
max_retries=_UPLOAD_MAX_RETRIES,
timeout=ClientTimeout(total=_UPLOAD_AND_DOWNLOAD_TIMEOUT),
)
_LOGGER.debug(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mill/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/mill",
"iot_class": "local_polling",
"loggers": ["mill", "mill_local"],
"requirements": ["millheater==0.13.1", "mill-local==0.3.0"]
"requirements": ["millheater==0.14.0", "mill-local==0.3.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/opower/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["opower"],
"quality_scale": "bronze",
"requirements": ["opower==0.15.5"]
"requirements": ["opower==0.15.6"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/portainer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from .coordinator import PortainerCoordinator

_PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SWITCH]
_PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SWITCH]

type PortainerConfigEntry = ConfigEntry[PortainerCoordinator]

Expand Down
128 changes: 128 additions & 0 deletions homeassistant/components/portainer/button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Support for Portainer buttons."""

from __future__ import annotations

from collections.abc import Callable, Coroutine
from dataclasses import dataclass
import logging
from typing import Any

from pyportainer import Portainer
from pyportainer.exceptions import (
PortainerAuthenticationError,
PortainerConnectionError,
PortainerTimeoutError,
)
from pyportainer.models.docker import DockerContainer

from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import PortainerConfigEntry
from .const import DOMAIN
from .coordinator import PortainerCoordinator, PortainerCoordinatorData
from .entity import PortainerContainerEntity

_LOGGER = logging.getLogger(__name__)


@dataclass(frozen=True, kw_only=True)
class PortainerButtonDescription(ButtonEntityDescription):
"""Class to describe a Portainer button entity."""

press_action: Callable[
[Portainer, int, str],
Coroutine[Any, Any, None],
]


BUTTONS: tuple[PortainerButtonDescription, ...] = (
PortainerButtonDescription(
key="restart",
name="Restart Container",
device_class=ButtonDeviceClass.RESTART,
entity_category=EntityCategory.CONFIG,
press_action=(
lambda portainer, endpoint_id, container_id: portainer.restart_container(
endpoint_id, container_id
)
),
),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: PortainerConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Portainer buttons."""
coordinator: PortainerCoordinator = entry.runtime_data

async_add_entities(
PortainerButton(
coordinator=coordinator,
entity_description=entity_description,
device_info=container,
via_device=endpoint,
)
for endpoint in coordinator.data.values()
for container in endpoint.containers.values()
for entity_description in BUTTONS
)


class PortainerButton(PortainerContainerEntity, ButtonEntity):
"""Defines a Portainer button."""

entity_description: PortainerButtonDescription

def __init__(
self,
coordinator: PortainerCoordinator,
entity_description: PortainerButtonDescription,
device_info: DockerContainer,
via_device: PortainerCoordinatorData,
) -> None:
"""Initialize the Portainer button entity."""
self.entity_description = entity_description
super().__init__(device_info, coordinator, via_device)

device_identifier = (
self._device_info.names[0].replace("/", " ").strip()
if self._device_info.names
else None
)
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{device_identifier}_{entity_description.key}"

async def async_press(self) -> None:
"""Trigger the Portainer button press service."""
try:
await self.entity_description.press_action(
self.coordinator.portainer, self.endpoint_id, self.device_id
)
except PortainerConnectionError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="cannot_connect",
translation_placeholders={"error": repr(err)},
) from err
except PortainerAuthenticationError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_auth",
translation_placeholders={"error": repr(err)},
) from err
except PortainerTimeoutError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="timeout_connect",
translation_placeholders={"error": repr(err)},
) from err
5 changes: 1 addition & 4 deletions homeassistant/components/portainer/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ rules:
unique-config-entry: done

# Silver
action-exceptions:
status: exempt
comment: |
No custom actions are defined.
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
Expand Down
Loading
Loading