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
13 changes: 13 additions & 0 deletions homeassistant/components/bring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,33 @@

from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType

from .const import DOMAIN
from .coordinator import (
BringActivityCoordinator,
BringConfigEntry,
BringCoordinators,
BringDataUpdateCoordinator,
)
from .services import async_setup_services

CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)

PLATFORMS: list[Platform] = [Platform.EVENT, Platform.SENSOR, Platform.TODO]

_LOGGER = logging.getLogger(__name__)


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Bring! services."""

async_setup_services(hass)
return True


async def async_setup_entry(hass: HomeAssistant, entry: BringConfigEntry) -> bool:
"""Set up Bring! from a config entry."""

Expand Down
5 changes: 4 additions & 1 deletion homeassistant/components/bring/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@
ATTR_SENDER: Final = "sender"
ATTR_ITEM_NAME: Final = "item"
ATTR_NOTIFICATION_TYPE: Final = "message"

ATTR_REACTION: Final = "reaction"
ATTR_ACTIVITY: Final = "uuid"
ATTR_RECEIVER: Final = "publicUserUuid"
SERVICE_PUSH_NOTIFICATION = "send_message"
SERVICE_ACTIVITY_STREAM_REACTION = "send_reaction"
3 changes: 3 additions & 0 deletions homeassistant/components/bring/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
"services": {
"send_message": {
"service": "mdi:cellphone-message"
},
"send_reaction": {
"service": "mdi:thumb-up"
}
}
}
110 changes: 110 additions & 0 deletions homeassistant/components/bring/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Actions for Bring! integration."""

import logging
from typing import TYPE_CHECKING

from bring_api import (
ActivityType,
BringAuthException,
BringNotificationType,
BringRequestException,
ReactionType,
)
import voluptuous as vol

from homeassistant.components.event import ATTR_EVENT_TYPE
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv, entity_registry as er

from .const import (
ATTR_ACTIVITY,
ATTR_REACTION,
ATTR_RECEIVER,
DOMAIN,
SERVICE_ACTIVITY_STREAM_REACTION,
)
from .coordinator import BringConfigEntry

_LOGGER = logging.getLogger(__name__)

SERVICE_ACTIVITY_STREAM_REACTION_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(ATTR_REACTION): vol.All(
vol.Upper,
vol.Coerce(ReactionType),
),
}
)


def get_config_entry(hass: HomeAssistant, entry_id: str) -> BringConfigEntry:
"""Return config entry or raise if not found or not loaded."""
entry = hass.config_entries.async_get_entry(entry_id)
if TYPE_CHECKING:
assert entry
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entry_not_loaded",
)
return entry


def async_setup_services(hass: HomeAssistant) -> None:
"""Set up services for Bring! integration."""

async def async_send_activity_stream_reaction(call: ServiceCall) -> None:
"""Send a reaction in response to recent activity of a list member."""

if (
not (state := hass.states.get(call.data[ATTR_ENTITY_ID]))
or not (entity := er.async_get(hass).async_get(call.data[ATTR_ENTITY_ID]))
or not entity.config_entry_id
):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entity_not_found",
translation_placeholders={
ATTR_ENTITY_ID: call.data[ATTR_ENTITY_ID],
},
)
config_entry = get_config_entry(hass, entity.config_entry_id)

coordinator = config_entry.runtime_data.data

list_uuid = entity.unique_id.split("_")[1]

activity = state.attributes[ATTR_EVENT_TYPE]

reaction: ReactionType = call.data[ATTR_REACTION]

if not activity:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="activity_not_found",
)
try:
await coordinator.bring.notify(
list_uuid,
BringNotificationType.LIST_ACTIVITY_STREAM_REACTION,
receiver=state.attributes[ATTR_RECEIVER],
activity=state.attributes[ATTR_ACTIVITY],
activity_type=ActivityType(activity.upper()),
reaction=reaction,
)
except (BringRequestException, BringAuthException) as e:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="reaction_request_failed",
) from e

hass.services.async_register(
DOMAIN,
SERVICE_ACTIVITY_STREAM_REACTION,
async_send_activity_stream_reaction,
SERVICE_ACTIVITY_STREAM_REACTION_SCHEMA,
)
25 changes: 25 additions & 0 deletions homeassistant/components/bring/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,28 @@ send_message:
required: false
selector:
text:
send_reaction:
fields:
entity_id:
required: true
selector:
entity:
filter:
- integration: bring
domain: event
example: event.shopping_list
reaction:
required: true
selector:
select:
options:
- label: 👍🏼
value: thumbs_up
- label: 🧐
value: monocle
- label: 🤤
value: drooling
- label: ❤️
value: heart
mode: dropdown
example: thumbs_up
27 changes: 27 additions & 0 deletions homeassistant/components/bring/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,19 @@
},
"notify_request_failed": {
"message": "Failed to send push notification for Bring! due to a connection error, try again later"
},
"reaction_request_failed": {
"message": "Failed to send reaction for Bring! due to a connection error, try again later"
},
"activity_not_found": {
"message": "Failed to send reaction for Bring! — No recent activity found"
},
"entity_not_found": {
"message": "Failed to send reaction for Bring! — Unknown entity {entity_id}"
},

"entry_not_loaded": {
"message": "The account associated with this Bring! list is either not loaded or disabled in Home Assistant."
}
},
"services": {
Expand All @@ -164,6 +177,20 @@
"description": "Item name(s) to include in an urgent message e.g. 'Attention! Attention! - We still urgently need: [Items]'"
}
}
},
"send_reaction": {
"name": "Send reaction",
"description": "Sends a reaction to a recent activity on a Bring! list by a member of the shared list.",
"fields": {
"entity_id": {
"name": "Activities",
"description": "Select the Bring! activities event entity for reacting to its most recent event"
},
"reaction": {
"name": "Reaction",
"description": "Type of reaction to send in response."
}
}
}
},
"selector": {
Expand Down
16 changes: 14 additions & 2 deletions homeassistant/components/diagnostics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
config_validation as cv,
device_registry as dr,
integration_platform,
issue_registry as ir,
)
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.json import (
Expand Down Expand Up @@ -187,6 +188,7 @@ def async_format_manifest(manifest: Manifest) -> Manifest:
async def _async_get_json_file_response(
hass: HomeAssistant,
data: Mapping[str, Any],
data_issues: list[dict[str, Any]] | None,
filename: str,
domain: str,
d_id: str,
Expand All @@ -213,6 +215,8 @@ async def _async_get_json_file_response(
"setup_times": async_get_domain_setup_times(hass, domain),
"data": data,
}
if data_issues is not None:
payload["issues"] = data_issues
try:
json_data = json.dumps(payload, indent=2, cls=ExtendedJSONEncoder)
except TypeError:
Expand Down Expand Up @@ -275,14 +279,22 @@ async def get(

filename = f"{config_entry.domain}-{config_entry.entry_id}"

issue_registry = ir.async_get(hass)
issues = issue_registry.issues
data_issues = [
issue_reg.to_json()
for issue_id, issue_reg in issues.items()
if issue_id[0] == config_entry.domain
]

if not device_diagnostics:
# Config entry diagnostics
if info.config_entry_diagnostics is None:
return web.Response(status=HTTPStatus.NOT_FOUND)
data = await info.config_entry_diagnostics(hass, config_entry)
filename = f"{DiagnosticsType.CONFIG_ENTRY}-{filename}"
return await _async_get_json_file_response(
hass, data, filename, config_entry.domain, d_id
hass, data, data_issues, filename, config_entry.domain, d_id
)

# Device diagnostics
Expand All @@ -300,5 +312,5 @@ async def get(

data = await info.device_diagnostics(hass, config_entry, device)
return await _async_get_json_file_response(
hass, data, filename, config_entry.domain, d_id, sub_id
hass, data, data_issues, filename, config_entry.domain, d_id, sub_id
)
13 changes: 9 additions & 4 deletions homeassistant/components/fritzbox/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,19 @@ async def _async_update_data(self) -> FritzboxCoordinatorData:

for device in new_data.devices.values():
# create device registry entry for new main devices
if (
device.ain not in self.data.devices
and device.device_and_unit_id[1] is None
if device.ain not in self.data.devices and (
device.device_and_unit_id[1] is None
or (
# workaround for sub units without a main device, e.g. Energy 250
# https://github.com/home-assistant/core/issues/145204
device.device_and_unit_id[1] == "1"
and device.device_and_unit_id[0] not in new_data.devices
)
):
dr.async_get(self.hass).async_get_or_create(
config_entry_id=self.config_entry.entry_id,
name=device.name,
identifiers={(DOMAIN, device.ain)},
identifiers={(DOMAIN, device.device_and_unit_id[0])},
manufacturer=device.manufacturer,
model=device.productname,
sw_version=device.fw_version,
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/gios/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

ATTR_C6H6: Final = "c6h6"
ATTR_CO: Final = "co"
ATTR_NO: Final = "no"
ATTR_NOX: Final = "nox"
ATTR_NO2: Final = "no2"
ATTR_O3: Final = "o3"
ATTR_PM10: Final = "pm10"
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/gios/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
"no2_index": {
"default": "mdi:molecule"
},
"nox": {
"default": "mdi:molecule"
},
"o3_index": {
"default": "mdi:molecule"
},
Expand Down
18 changes: 18 additions & 0 deletions homeassistant/components/gios/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
ATTR_AQI,
ATTR_C6H6,
ATTR_CO,
ATTR_NO,
ATTR_NO2,
ATTR_NOX,
ATTR_O3,
ATTR_PM10,
ATTR_PM25,
Expand Down Expand Up @@ -74,6 +76,14 @@ class GiosSensorEntityDescription(SensorEntityDescription):
state_class=SensorStateClass.MEASUREMENT,
translation_key="co",
),
GiosSensorEntityDescription(
key=ATTR_NO,
value=lambda sensors: sensors.no.value if sensors.no else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.NITROGEN_MONOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
key=ATTR_NO2,
value=lambda sensors: sensors.no2.value if sensors.no2 else None,
Expand All @@ -90,6 +100,14 @@ class GiosSensorEntityDescription(SensorEntityDescription):
options=["very_bad", "bad", "sufficient", "moderate", "good", "very_good"],
translation_key="no2_index",
),
GiosSensorEntityDescription(
key=ATTR_NOX,
translation_key=ATTR_NOX,
value=lambda sensors: sensors.nox.value if sensors.nox else None,
suggested_display_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
key=ATTR_O3,
value=lambda sensors: sensors.o3.value if sensors.o3 else None,
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/gios/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
}
}
},
"nox": {
"name": "Nitrogen oxides"
},
"o3_index": {
"name": "Ozone index",
"state": {
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/husqvarna_automower/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_push",
"loggers": ["aioautomower"],
"quality_scale": "silver",
"requirements": ["aioautomower==1.2.0"]
"requirements": ["aioautomower==1.2.2"]
}
Loading
Loading