Skip to content

Commit 1554476

Browse files
authored
Add action for activity reactions to Bring! (home-assistant#138175)
1 parent 3307132 commit 1554476

7 files changed

Lines changed: 372 additions & 1 deletion

File tree

homeassistant/components/bring/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,33 @@
88

99
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
1010
from homeassistant.core import HomeAssistant
11+
from homeassistant.helpers import config_validation as cv
1112
from homeassistant.helpers.aiohttp_client import async_get_clientsession
13+
from homeassistant.helpers.typing import ConfigType
1214

15+
from .const import DOMAIN
1316
from .coordinator import (
1417
BringActivityCoordinator,
1518
BringConfigEntry,
1619
BringCoordinators,
1720
BringDataUpdateCoordinator,
1821
)
22+
from .services import async_setup_services
23+
24+
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
1925

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

2228
_LOGGER = logging.getLogger(__name__)
2329

2430

31+
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
32+
"""Set up the Bring! services."""
33+
34+
async_setup_services(hass)
35+
return True
36+
37+
2538
async def async_setup_entry(hass: HomeAssistant, entry: BringConfigEntry) -> bool:
2639
"""Set up Bring! from a config entry."""
2740

homeassistant/components/bring/const.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
ATTR_SENDER: Final = "sender"
88
ATTR_ITEM_NAME: Final = "item"
99
ATTR_NOTIFICATION_TYPE: Final = "message"
10-
10+
ATTR_REACTION: Final = "reaction"
11+
ATTR_ACTIVITY: Final = "uuid"
12+
ATTR_RECEIVER: Final = "publicUserUuid"
1113
SERVICE_PUSH_NOTIFICATION = "send_message"
14+
SERVICE_ACTIVITY_STREAM_REACTION = "send_reaction"

homeassistant/components/bring/icons.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
"services": {
3636
"send_message": {
3737
"service": "mdi:cellphone-message"
38+
},
39+
"send_reaction": {
40+
"service": "mdi:thumb-up"
3841
}
3942
}
4043
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""Actions for Bring! integration."""
2+
3+
import logging
4+
from typing import TYPE_CHECKING
5+
6+
from bring_api import (
7+
ActivityType,
8+
BringAuthException,
9+
BringNotificationType,
10+
BringRequestException,
11+
ReactionType,
12+
)
13+
import voluptuous as vol
14+
15+
from homeassistant.components.event import ATTR_EVENT_TYPE
16+
from homeassistant.config_entries import ConfigEntryState
17+
from homeassistant.const import ATTR_ENTITY_ID
18+
from homeassistant.core import HomeAssistant, ServiceCall
19+
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
20+
from homeassistant.helpers import config_validation as cv, entity_registry as er
21+
22+
from .const import (
23+
ATTR_ACTIVITY,
24+
ATTR_REACTION,
25+
ATTR_RECEIVER,
26+
DOMAIN,
27+
SERVICE_ACTIVITY_STREAM_REACTION,
28+
)
29+
from .coordinator import BringConfigEntry
30+
31+
_LOGGER = logging.getLogger(__name__)
32+
33+
SERVICE_ACTIVITY_STREAM_REACTION_SCHEMA = vol.Schema(
34+
{
35+
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
36+
vol.Required(ATTR_REACTION): vol.All(
37+
vol.Upper,
38+
vol.Coerce(ReactionType),
39+
),
40+
}
41+
)
42+
43+
44+
def get_config_entry(hass: HomeAssistant, entry_id: str) -> BringConfigEntry:
45+
"""Return config entry or raise if not found or not loaded."""
46+
entry = hass.config_entries.async_get_entry(entry_id)
47+
if TYPE_CHECKING:
48+
assert entry
49+
if entry.state is not ConfigEntryState.LOADED:
50+
raise ServiceValidationError(
51+
translation_domain=DOMAIN,
52+
translation_key="entry_not_loaded",
53+
)
54+
return entry
55+
56+
57+
def async_setup_services(hass: HomeAssistant) -> None:
58+
"""Set up services for Bring! integration."""
59+
60+
async def async_send_activity_stream_reaction(call: ServiceCall) -> None:
61+
"""Send a reaction in response to recent activity of a list member."""
62+
63+
if (
64+
not (state := hass.states.get(call.data[ATTR_ENTITY_ID]))
65+
or not (entity := er.async_get(hass).async_get(call.data[ATTR_ENTITY_ID]))
66+
or not entity.config_entry_id
67+
):
68+
raise ServiceValidationError(
69+
translation_domain=DOMAIN,
70+
translation_key="entity_not_found",
71+
translation_placeholders={
72+
ATTR_ENTITY_ID: call.data[ATTR_ENTITY_ID],
73+
},
74+
)
75+
config_entry = get_config_entry(hass, entity.config_entry_id)
76+
77+
coordinator = config_entry.runtime_data.data
78+
79+
list_uuid = entity.unique_id.split("_")[1]
80+
81+
activity = state.attributes[ATTR_EVENT_TYPE]
82+
83+
reaction: ReactionType = call.data[ATTR_REACTION]
84+
85+
if not activity:
86+
raise ServiceValidationError(
87+
translation_domain=DOMAIN,
88+
translation_key="activity_not_found",
89+
)
90+
try:
91+
await coordinator.bring.notify(
92+
list_uuid,
93+
BringNotificationType.LIST_ACTIVITY_STREAM_REACTION,
94+
receiver=state.attributes[ATTR_RECEIVER],
95+
activity=state.attributes[ATTR_ACTIVITY],
96+
activity_type=ActivityType(activity.upper()),
97+
reaction=reaction,
98+
)
99+
except (BringRequestException, BringAuthException) as e:
100+
raise HomeAssistantError(
101+
translation_domain=DOMAIN,
102+
translation_key="reaction_request_failed",
103+
) from e
104+
105+
hass.services.async_register(
106+
DOMAIN,
107+
SERVICE_ACTIVITY_STREAM_REACTION,
108+
async_send_activity_stream_reaction,
109+
SERVICE_ACTIVITY_STREAM_REACTION_SCHEMA,
110+
)

homeassistant/components/bring/services.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,28 @@ send_message:
2121
required: false
2222
selector:
2323
text:
24+
send_reaction:
25+
fields:
26+
entity_id:
27+
required: true
28+
selector:
29+
entity:
30+
filter:
31+
- integration: bring
32+
domain: event
33+
example: event.shopping_list
34+
reaction:
35+
required: true
36+
selector:
37+
select:
38+
options:
39+
- label: 👍🏼
40+
value: thumbs_up
41+
- label: 🧐
42+
value: monocle
43+
- label: 🤤
44+
value: drooling
45+
- label: ❤️
46+
value: heart
47+
mode: dropdown
48+
example: thumbs_up

homeassistant/components/bring/strings.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@
144144
},
145145
"notify_request_failed": {
146146
"message": "Failed to send push notification for Bring! due to a connection error, try again later"
147+
},
148+
"reaction_request_failed": {
149+
"message": "Failed to send reaction for Bring! due to a connection error, try again later"
150+
},
151+
"activity_not_found": {
152+
"message": "Failed to send reaction for Bring! — No recent activity found"
153+
},
154+
"entity_not_found": {
155+
"message": "Failed to send reaction for Bring! — Unknown entity {entity_id}"
156+
},
157+
158+
"entry_not_loaded": {
159+
"message": "The account associated with this Bring! list is either not loaded or disabled in Home Assistant."
147160
}
148161
},
149162
"services": {
@@ -164,6 +177,20 @@
164177
"description": "Item name(s) to include in an urgent message e.g. 'Attention! Attention! - We still urgently need: [Items]'"
165178
}
166179
}
180+
},
181+
"send_reaction": {
182+
"name": "Send reaction",
183+
"description": "Sends a reaction to a recent activity on a Bring! list by a member of the shared list.",
184+
"fields": {
185+
"entity_id": {
186+
"name": "Activities",
187+
"description": "Select the Bring! activities event entity for reacting to its most recent event"
188+
},
189+
"reaction": {
190+
"name": "Reaction",
191+
"description": "Type of reaction to send in response."
192+
}
193+
}
167194
}
168195
},
169196
"selector": {

0 commit comments

Comments
 (0)