From 1eab5707a5efee32889639aabdbd16ebecb13596 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Wed, 10 Sep 2025 01:34:26 +0000 Subject: [PATCH 1/9] Add support for button actions to ntfy --- homeassistant/components/ntfy/icons.json | 5 +- homeassistant/components/ntfy/notify.py | 71 ++++++++++++- homeassistant/components/ntfy/services.yaml | 112 ++++++++++++++++++++ homeassistant/components/ntfy/strings.json | 21 ++++ 4 files changed, 205 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/ntfy/icons.json b/homeassistant/components/ntfy/icons.json index 4b04a16f69f5f..1f2ab5ef5f6d0 100644 --- a/homeassistant/components/ntfy/icons.json +++ b/homeassistant/components/ntfy/icons.json @@ -75,7 +75,10 @@ }, "services": { "publish": { - "service": "mdi:send" + "service": "mdi:send", + "sections": { + "actions": "mdi:gesture-tap-button" + } } } } diff --git a/homeassistant/components/ntfy/notify.py b/homeassistant/components/ntfy/notify.py index 176dddd7a44ce..80ada498a33f9 100644 --- a/homeassistant/components/ntfy/notify.py +++ b/homeassistant/components/ntfy/notify.py @@ -5,7 +5,7 @@ from datetime import timedelta from typing import Any -from aiontfy import Message +from aiontfy import BroadcastAction, HttpAction, Message, ViewAction from aiontfy.exceptions import ( NtfyException, NtfyHTTPError, @@ -31,7 +31,7 @@ from .entity import NtfyBaseEntity PARALLEL_UPDATES = 0 - +MAX_ACTIONS_ALLOWED = 3 SERVICE_PUBLISH = "publish" ATTR_ATTACH = "attach" @@ -43,7 +43,53 @@ ATTR_MARKDOWN = "markdown" ATTR_PRIORITY = "priority" ATTR_TAGS = "tags" - +ATTR_ACTION = "action" +ATTR_VIEW = "view" +ATTR_BROADCAST = "broadcast" +ATTR_HTTP = "http" +ATTR_LABEL = "label" +ATTR_URL = "url" +ATTR_CLEAR = "clear" +ATTR_POSITION = "position" +ATTR_INTENT = "intent" +ATTR_EXTRAS = "extras" +ATTR_METHOD = "method" +ATTR_HEADERS = "headers" +ATTR_BODY = "body" +ACTIONS_MAP = { + ATTR_VIEW: ViewAction, + ATTR_BROADCAST: BroadcastAction, + ATTR_HTTP: HttpAction, +} + +ACTION_SCHEMA = vol.Schema( + { + vol.Required(ATTR_LABEL): cv.string, + vol.Optional(ATTR_CLEAR, default=False): cv.boolean, + vol.Optional(ATTR_POSITION): vol.All(vol.Coerce(int), vol.Range(1, 3)), + } +) +VIEW_SCHEMA = ACTION_SCHEMA.extend( + { + vol.Optional(ATTR_ACTION, default="view"): str, + vol.Required(ATTR_URL): cv.url, + } +) +BROADCAST_SCHEMA = ACTION_SCHEMA.extend( + { + vol.Optional(ATTR_ACTION, default="broadcast"): str, + vol.Optional(ATTR_INTENT): cv.string, + vol.Optional(ATTR_EXTRAS): dict[str, str], + } +) +HTTP_SCHEMA = VIEW_SCHEMA.extend( + { + vol.Optional(ATTR_ACTION, default="http"): str, + vol.Optional(ATTR_METHOD): cv.string, + vol.Optional(ATTR_HEADERS): dict[str, str], + vol.Optional(ATTR_BODY): cv.string, + } +) SERVICE_PUBLISH_SCHEMA = cv.make_entity_service_schema( { vol.Optional(ATTR_TITLE): cv.string, @@ -60,6 +106,9 @@ vol.Optional(ATTR_EMAIL): vol.Email(), vol.Optional(ATTR_CALL): cv.string, vol.Optional(ATTR_ICON): vol.All(vol.Url(), vol.Coerce(URL)), + vol.Optional(ATTR_VIEW): vol.All(cv.ensure_list, [VIEW_SCHEMA]), + vol.Optional(ATTR_BROADCAST): vol.All(cv.ensure_list, [BROADCAST_SCHEMA]), + vol.Optional(ATTR_HTTP): vol.All(cv.ensure_list, [HTTP_SCHEMA]), } ) @@ -115,6 +164,22 @@ async def publish(self, **kwargs: Any) -> None: translation_domain=DOMAIN, translation_key="delay_no_call", ) + actions: list[dict[str, Any]] = ( + params.pop(ATTR_VIEW, []) + + params.pop(ATTR_BROADCAST, []) + + params.pop(ATTR_HTTP, []) + ) + actions.sort(key=lambda a: a.pop(ATTR_POSITION, float("inf"))) + + if actions: + if len(actions) > MAX_ACTIONS_ALLOWED: + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="too_many_actions", + ) + params["actions"] = [ + ACTIONS_MAP[action.pop(ATTR_ACTION)](**action) for action in actions + ] msg = Message(topic=self.topic, **params) try: diff --git a/homeassistant/components/ntfy/services.yaml b/homeassistant/components/ntfy/services.yaml index 2c8e00746e5a2..bdc9578468674 100644 --- a/homeassistant/components/ntfy/services.yaml +++ b/homeassistant/components/ntfy/services.yaml @@ -88,3 +88,115 @@ publish: type: url autocomplete: url example: https://example.org/logo.png + actions: + collapsed: true + fields: + view: + selector: + object: + label_field: "label" + description_field: "url" + multiple: true + fields: + label: + label: Label + selector: + text: + url: + label: URL + selector: + text: + type: url + clear: + label: Clear + selector: + boolean: + position: + label: Position + selector: + select: + options: + - 1 + - 2 + - 3 + mode: dropdown + broadcast: + selector: + object: + label_field: "label" + description_field: "intent" + multiple: true + fields: + label: + label: Label + selector: + text: + intent: + label: Android intent + selector: + text: + extras: + label: Intent extras + selector: + object: + clear: + label: Clear + selector: + boolean: + position: + label: Position + selector: + select: + options: + - 1 + - 2 + - 3 + mode: dropdown + http: + selector: + object: + label_field: "label" + description_field: "url" + multiple: true + fields: + label: + label: Label + selector: + text: + url: + label: URL + selector: + text: + type: url + method: + label: HTTP method + selector: + select: + options: + - GET + - POST + - PUT + - DELETE + custom_value: true + headers: + label: HTTP headers + selector: + object: + body: + label: HTTP body + selector: + text: + multiline: true + clear: + label: Clear + selector: + boolean: + position: + label: Position + selector: + select: + options: + - 1 + - 2 + - 3 + mode: dropdown diff --git a/homeassistant/components/ntfy/strings.json b/homeassistant/components/ntfy/strings.json index 86d59e0dc6c0b..44c40db831a1a 100644 --- a/homeassistant/components/ntfy/strings.json +++ b/homeassistant/components/ntfy/strings.json @@ -290,6 +290,9 @@ }, "delay_no_call": { "message": "Delayed call notifications are not supported" + }, + "too_many_actions": { + "message": "Too many actions defined. Only 3 allowed" } }, "services": { @@ -340,6 +343,24 @@ "icon": { "name": "Icon URL", "description": "Include an icon that will appear next to the text of the notification. Only JPEG and PNG images are supported." + }, + "view": { + "name": "Open website/app", + "description": "Adds a 'view' button action that opens a website or app when the action button is tapped. See the documentation for details: https://docs.ntfy.sh/publish/#open-websiteapp" + }, + "broadcast": { + "name": "Send Android broadcast", + "description": "Adds a 'broadcast' button action that sends an Android broadcast intent when the action button is tapped. This action is only supported on Android. See the documentation for details: https://docs.ntfy.sh/publish/#send-android-broadcast" + }, + "http": { + "name": "Send HTTP request", + "description": "Adds an 'http' button action that sends an HTTP request when the action button is tapped. See the documentation for details: https://docs.ntfy.sh/publish/#send-http-request" + } + }, + "sections": { + "actions": { + "name": "Action buttons", + "description": "Up to three actions ('view', 'broadcast', or 'http') can be defined for a notification. These appear as buttons below the notification content. Actions are executed when the corresponding button is tapped or clicked." } } } From 09edf42349639ac1c7931a9669e27547870f6889 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Wed, 10 Sep 2025 02:10:27 +0000 Subject: [PATCH 2/9] tests --- tests/components/ntfy/test_services.py | 65 +++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/tests/components/ntfy/test_services.py b/tests/components/ntfy/test_services.py index d07df40264f6a..30751be81b6af 100644 --- a/tests/components/ntfy/test_services.py +++ b/tests/components/ntfy/test_services.py @@ -2,7 +2,7 @@ from typing import Any -from aiontfy import Message +from aiontfy import BroadcastAction, HttpAction, Message, ViewAction from aiontfy.exceptions import ( NtfyException, NtfyHTTPError, @@ -15,14 +15,17 @@ from homeassistant.components.ntfy.const import DOMAIN from homeassistant.components.ntfy.notify import ( ATTR_ATTACH, + ATTR_BROADCAST, ATTR_CALL, ATTR_CLICK, ATTR_DELAY, ATTR_EMAIL, + ATTR_HTTP, ATTR_ICON, ATTR_MARKDOWN, ATTR_PRIORITY, ATTR_TAGS, + ATTR_VIEW, SERVICE_PUBLISH, ) from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState @@ -60,6 +63,32 @@ async def test_ntfy_publish( ATTR_MARKDOWN: True, ATTR_PRIORITY: "5", ATTR_TAGS: ["partying_face", "grin"], + ATTR_HTTP: [ + { + "label": "Close door", + "url": "https://api.example.local/", + "method": "PUT", + "headers": {"Authorization": "Bearer ..."}, + "clear": False, + } + ], + ATTR_VIEW: [ + { + "label": "Open website", + "url": "https://example.com", + "position": 3, + "clear": False, + } + ], + ATTR_BROADCAST: [ + { + "label": "Take picture", + "intent": "com.example.AN_INTENT", + "extras": {"cmd": "pic"}, + "position": 1, + "clear": True, + } + ], }, blocking=True, ) @@ -76,6 +105,27 @@ async def test_ntfy_publish( markdown=True, icon=URL("https://example.org/logo.png"), delay="86430.0s", + actions=[ + BroadcastAction( + label="Take picture", + intent="com.example.AN_INTENT", + extras={"cmd": "pic"}, + clear=True, + ), + ViewAction( + label="Open website", + url="https://example.com", + clear=False, + ), + HttpAction( + label="Close door", + url="https://api.example.local/", + method="PUT", + headers={"Authorization": "Bearer ..."}, + body=None, + clear=False, + ), + ], ) ) @@ -142,12 +192,23 @@ async def test_send_message_exception( {ATTR_DELAY: {"days": 1, "seconds": 30}, ATTR_EMAIL: "mail@example.org"}, "Delayed email notifications are not supported", ), + ( + { + ATTR_BROADCAST: [ + {"label": "1"}, + {"label": "2"}, + {"label": "3"}, + {"label": "4"}, + ], + }, + "Too many actions defined. Only 3 allowed", + ), ], ) +@pytest.mark.usefixtures("mock_aiontfy") async def test_send_message_validation_errors( hass: HomeAssistant, config_entry: MockConfigEntry, - mock_aiontfy: AsyncMock, payload: dict[str, Any], error_msg: str, ) -> None: From dc3375b4fed0a093fdd1eda576a5c0e72f585fd7 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Wed, 10 Sep 2025 03:20:31 +0000 Subject: [PATCH 3/9] selector translations --- homeassistant/components/ntfy/services.yaml | 19 +++----------- homeassistant/components/ntfy/strings.json | 28 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/ntfy/services.yaml b/homeassistant/components/ntfy/services.yaml index bdc9578468674..e6a7989669124 100644 --- a/homeassistant/components/ntfy/services.yaml +++ b/homeassistant/components/ntfy/services.yaml @@ -97,22 +97,19 @@ publish: label_field: "label" description_field: "url" multiple: true + translation_key: view fields: label: - label: Label selector: text: url: - label: URL selector: text: type: url clear: - label: Clear selector: boolean: position: - label: Position selector: select: options: @@ -126,25 +123,21 @@ publish: label_field: "label" description_field: "intent" multiple: true + translation_key: broadcast fields: label: - label: Label selector: text: intent: - label: Android intent selector: text: extras: - label: Intent extras selector: object: clear: - label: Clear selector: boolean: position: - label: Position selector: select: options: @@ -158,18 +151,16 @@ publish: label_field: "label" description_field: "url" multiple: true + translation_key: http fields: label: - label: Label selector: text: url: - label: URL selector: text: type: url method: - label: HTTP method selector: select: options: @@ -179,20 +170,16 @@ publish: - DELETE custom_value: true headers: - label: HTTP headers selector: object: body: - label: HTTP body selector: text: multiline: true clear: - label: Clear selector: boolean: position: - label: Position selector: select: options: diff --git a/homeassistant/components/ntfy/strings.json b/homeassistant/components/ntfy/strings.json index 44c40db831a1a..267055d47567f 100644 --- a/homeassistant/components/ntfy/strings.json +++ b/homeassistant/components/ntfy/strings.json @@ -374,6 +374,34 @@ "4": "[%key:common::state::high%]", "5": "Maximum" } + }, + "view": { + "fields": { + "label": "Label of the action button", + "url": "URL to open when action is tapped", + "clear": "Clear notification after action button is tapped", + "position": "Position of the action button" + } + }, + "broadcast": { + "fields": { + "label": "[%key:component::ntfy::selector::view::fields::label%]", + "intent": "Android intent to send when action is tapped", + "extras": "Extras to include in the intent (key-value pairs)", + "clear": "[%key:component::ntfy::selector::view::fields::clear%]", + "position": "[%key:component::ntfy::selector::view::fields::position%]" + } + }, + "http": { + "fields": { + "label": "[%key:component::ntfy::selector::view::fields::label%]", + "url": "URL to which the HTTP request will be sent", + "method": "HTTP method to use for request", + "headers": "Additional HTTP headers (key-value pairs)", + "body": "Body of the HTTP request", + "clear": "[%key:component::ntfy::selector::view::fields::clear%]", + "position": "[%key:component::ntfy::selector::view::fields::position%]" + } } }, "issues": { From e241d7ee542b59d0223e4d616f1d17f702bf2a33 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:00:49 +0000 Subject: [PATCH 4/9] rework object selector schema --- homeassistant/components/ntfy/notify.py | 21 ++- homeassistant/components/ntfy/services.yaml | 152 +++++++------------- homeassistant/components/ntfy/strings.json | 45 +++--- tests/components/ntfy/test_services.py | 49 +++---- 4 files changed, 102 insertions(+), 165 deletions(-) diff --git a/homeassistant/components/ntfy/notify.py b/homeassistant/components/ntfy/notify.py index 80ada498a33f9..1d0d34bb9207d 100644 --- a/homeassistant/components/ntfy/notify.py +++ b/homeassistant/components/ntfy/notify.py @@ -43,6 +43,7 @@ ATTR_MARKDOWN = "markdown" ATTR_PRIORITY = "priority" ATTR_TAGS = "tags" +ATTR_ACTIONS = "actions" ATTR_ACTION = "action" ATTR_VIEW = "view" ATTR_BROADCAST = "broadcast" @@ -66,25 +67,24 @@ { vol.Required(ATTR_LABEL): cv.string, vol.Optional(ATTR_CLEAR, default=False): cv.boolean, - vol.Optional(ATTR_POSITION): vol.All(vol.Coerce(int), vol.Range(1, 3)), } ) VIEW_SCHEMA = ACTION_SCHEMA.extend( { - vol.Optional(ATTR_ACTION, default="view"): str, + vol.Optional(ATTR_ACTION): vol.All(str, "view"), vol.Required(ATTR_URL): cv.url, } ) BROADCAST_SCHEMA = ACTION_SCHEMA.extend( { - vol.Optional(ATTR_ACTION, default="broadcast"): str, + vol.Optional(ATTR_ACTION): vol.All(str, "broadcast"), vol.Optional(ATTR_INTENT): cv.string, vol.Optional(ATTR_EXTRAS): dict[str, str], } ) HTTP_SCHEMA = VIEW_SCHEMA.extend( { - vol.Optional(ATTR_ACTION, default="http"): str, + vol.Optional(ATTR_ACTION): vol.All(str, "http"), vol.Optional(ATTR_METHOD): cv.string, vol.Optional(ATTR_HEADERS): dict[str, str], vol.Optional(ATTR_BODY): cv.string, @@ -106,9 +106,9 @@ vol.Optional(ATTR_EMAIL): vol.Email(), vol.Optional(ATTR_CALL): cv.string, vol.Optional(ATTR_ICON): vol.All(vol.Url(), vol.Coerce(URL)), - vol.Optional(ATTR_VIEW): vol.All(cv.ensure_list, [VIEW_SCHEMA]), - vol.Optional(ATTR_BROADCAST): vol.All(cv.ensure_list, [BROADCAST_SCHEMA]), - vol.Optional(ATTR_HTTP): vol.All(cv.ensure_list, [HTTP_SCHEMA]), + vol.Optional(ATTR_ACTIONS): vol.All( + cv.ensure_list, [vol.Any(VIEW_SCHEMA, BROADCAST_SCHEMA, HTTP_SCHEMA)] + ), } ) @@ -164,13 +164,8 @@ async def publish(self, **kwargs: Any) -> None: translation_domain=DOMAIN, translation_key="delay_no_call", ) - actions: list[dict[str, Any]] = ( - params.pop(ATTR_VIEW, []) - + params.pop(ATTR_BROADCAST, []) - + params.pop(ATTR_HTTP, []) - ) - actions.sort(key=lambda a: a.pop(ATTR_POSITION, float("inf"))) + actions: list[dict[str, Any]] | None = params.get(ATTR_ACTIONS) if actions: if len(actions) > MAX_ACTIONS_ALLOWED: raise ServiceValidationError( diff --git a/homeassistant/components/ntfy/services.yaml b/homeassistant/components/ntfy/services.yaml index e6a7989669124..1719221deb7b1 100644 --- a/homeassistant/components/ntfy/services.yaml +++ b/homeassistant/components/ntfy/services.yaml @@ -89,101 +89,57 @@ publish: autocomplete: url example: https://example.org/logo.png actions: - collapsed: true - fields: - view: - selector: - object: - label_field: "label" - description_field: "url" - multiple: true - translation_key: view - fields: - label: - selector: - text: - url: - selector: - text: - type: url - clear: - selector: - boolean: - position: - selector: - select: - options: - - 1 - - 2 - - 3 - mode: dropdown - broadcast: - selector: - object: - label_field: "label" - description_field: "intent" - multiple: true - translation_key: broadcast - fields: - label: - selector: - text: - intent: - selector: - text: - extras: - selector: - object: - clear: - selector: - boolean: - position: - selector: - select: - options: - - 1 - - 2 - - 3 - mode: dropdown - http: - selector: - object: - label_field: "label" - description_field: "url" - multiple: true - translation_key: http - fields: - label: - selector: - text: - url: - selector: - text: - type: url - method: - selector: - select: - options: - - GET - - POST - - PUT - - DELETE - custom_value: true - headers: - selector: - object: - body: - selector: - text: - multiline: true - clear: - selector: - boolean: - position: - selector: - select: - options: - - 1 - - 2 - - 3 - mode: dropdown + selector: + object: + label_field: "label" + description_field: "url" + multiple: true + translation_key: actions + fields: + action: + selector: + select: + options: + - value: view + label: Open website/app + - value: broadcast + label: Send Android broadcast + - value: http + label: Send HTTP request + translation_key: action_type + mode: dropdown + label: + selector: + text: + required: true + url: + selector: + text: + type: url + required: true + intent: + selector: + text: + required: true + extras: + selector: + object: + method: + selector: + select: + options: + - GET + - POST + - PUT + - DELETE + custom_value: true + headers: + selector: + object: + body: + selector: + text: + multiline: true + clear: + selector: + boolean: diff --git a/homeassistant/components/ntfy/strings.json b/homeassistant/components/ntfy/strings.json index 267055d47567f..5b52cca719522 100644 --- a/homeassistant/components/ntfy/strings.json +++ b/homeassistant/components/ntfy/strings.json @@ -355,12 +355,11 @@ "http": { "name": "Send HTTP request", "description": "Adds an 'http' button action that sends an HTTP request when the action button is tapped. See the documentation for details: https://docs.ntfy.sh/publish/#send-http-request" - } - }, - "sections": { + }, + "actions": { "name": "Action buttons", - "description": "Up to three actions ('view', 'broadcast', or 'http') can be defined for a notification. These appear as buttons below the notification content. Actions are executed when the corresponding button is tapped or clicked." + "description": "Up to three actions ('view', 'broadcast', or 'http') can be added as buttons below the notification. Actions are executed when the corresponding button is tapped or clicked." } } } @@ -375,32 +374,24 @@ "5": "Maximum" } }, - "view": { - "fields": { - "label": "Label of the action button", - "url": "URL to open when action is tapped", - "clear": "Clear notification after action button is tapped", - "position": "Position of the action button" - } - }, - "broadcast": { - "fields": { - "label": "[%key:component::ntfy::selector::view::fields::label%]", - "intent": "Android intent to send when action is tapped", - "extras": "Extras to include in the intent (key-value pairs)", - "clear": "[%key:component::ntfy::selector::view::fields::clear%]", - "position": "[%key:component::ntfy::selector::view::fields::position%]" + "action_type": { + "options": { + "view": "Open website/app", + "broadcast": "Send Android broadcast", + "http": "Send HTTP request" } }, - "http": { + "actions": { "fields": { - "label": "[%key:component::ntfy::selector::view::fields::label%]", - "url": "URL to which the HTTP request will be sent", - "method": "HTTP method to use for request", - "headers": "Additional HTTP headers (key-value pairs)", - "body": "Body of the HTTP request", - "clear": "[%key:component::ntfy::selector::view::fields::clear%]", - "position": "[%key:component::ntfy::selector::view::fields::position%]" + "label": "Label of the action button", + "action": "Action type", + "url": "URL to open for the 'view' action or to request for the 'http' action", + "intent": "Android intent to send when the 'broadcast' action is triggered", + "extras": "Extras to include in the intent as key-value pairs ('broadcast' action only)", + "method": "HTTP method to use for the 'http' action", + "headers": "Additional HTTP headers as key-value pairs ('http' action only)", + "body": "Body of the HTTP request ('http' action only)", + "clear": "Clear notification after action button is tapped" } } }, diff --git a/tests/components/ntfy/test_services.py b/tests/components/ntfy/test_services.py index 30751be81b6af..b8859193d5f21 100644 --- a/tests/components/ntfy/test_services.py +++ b/tests/components/ntfy/test_services.py @@ -14,18 +14,16 @@ from homeassistant.components.notify import ATTR_MESSAGE, ATTR_TITLE from homeassistant.components.ntfy.const import DOMAIN from homeassistant.components.ntfy.notify import ( + ATTR_ACTIONS, ATTR_ATTACH, - ATTR_BROADCAST, ATTR_CALL, ATTR_CLICK, ATTR_DELAY, ATTR_EMAIL, - ATTR_HTTP, ATTR_ICON, ATTR_MARKDOWN, ATTR_PRIORITY, ATTR_TAGS, - ATTR_VIEW, SERVICE_PUBLISH, ) from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState @@ -63,31 +61,28 @@ async def test_ntfy_publish( ATTR_MARKDOWN: True, ATTR_PRIORITY: "5", ATTR_TAGS: ["partying_face", "grin"], - ATTR_HTTP: [ + ATTR_ACTIONS: [ { - "label": "Close door", - "url": "https://api.example.local/", - "method": "PUT", - "headers": {"Authorization": "Bearer ..."}, - "clear": False, - } - ], - ATTR_VIEW: [ + "action": "broadcast", + "label": "Take picture", + "intent": "com.example.AN_INTENT", + "extras": {"cmd": "pic"}, + "clear": True, + }, { + "action": "view", "label": "Open website", "url": "https://example.com", - "position": 3, "clear": False, - } - ], - ATTR_BROADCAST: [ + }, { - "label": "Take picture", - "intent": "com.example.AN_INTENT", - "extras": {"cmd": "pic"}, - "position": 1, - "clear": True, - } + "action": "http", + "label": "Close door", + "url": "https://api.example.local/", + "method": "PUT", + "headers": {"Authorization": "Bearer ..."}, + "clear": False, + }, ], }, blocking=True, @@ -194,11 +189,11 @@ async def test_send_message_exception( ), ( { - ATTR_BROADCAST: [ - {"label": "1"}, - {"label": "2"}, - {"label": "3"}, - {"label": "4"}, + ATTR_ACTIONS: [ + {"action": "broadcast", "label": "1"}, + {"action": "broadcast", "label": "2"}, + {"action": "broadcast", "label": "3"}, + {"action": "broadcast", "label": "4"}, ], }, "Too many actions defined. Only 3 allowed", From e987db5031febb0ceb7c9cda6779d623fe1447dc Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:22:07 +0000 Subject: [PATCH 5/9] comment --- homeassistant/components/ntfy/notify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/ntfy/notify.py b/homeassistant/components/ntfy/notify.py index 1d0d34bb9207d..7d25e8cb56917 100644 --- a/homeassistant/components/ntfy/notify.py +++ b/homeassistant/components/ntfy/notify.py @@ -31,7 +31,7 @@ from .entity import NtfyBaseEntity PARALLEL_UPDATES = 0 -MAX_ACTIONS_ALLOWED = 3 +MAX_ACTIONS_ALLOWED = 3 # ntfy only supports up to 3 actions per notification SERVICE_PUBLISH = "publish" ATTR_ATTACH = "attach" From 391bfbb0dd5d54874b14a1c34890619c04f41b12 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Mon, 15 Sep 2025 20:41:04 +0000 Subject: [PATCH 6/9] remove unused strings --- homeassistant/components/ntfy/strings.json | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/homeassistant/components/ntfy/strings.json b/homeassistant/components/ntfy/strings.json index 5b52cca719522..feeb8e319c3c8 100644 --- a/homeassistant/components/ntfy/strings.json +++ b/homeassistant/components/ntfy/strings.json @@ -344,19 +344,6 @@ "name": "Icon URL", "description": "Include an icon that will appear next to the text of the notification. Only JPEG and PNG images are supported." }, - "view": { - "name": "Open website/app", - "description": "Adds a 'view' button action that opens a website or app when the action button is tapped. See the documentation for details: https://docs.ntfy.sh/publish/#open-websiteapp" - }, - "broadcast": { - "name": "Send Android broadcast", - "description": "Adds a 'broadcast' button action that sends an Android broadcast intent when the action button is tapped. This action is only supported on Android. See the documentation for details: https://docs.ntfy.sh/publish/#send-android-broadcast" - }, - "http": { - "name": "Send HTTP request", - "description": "Adds an 'http' button action that sends an HTTP request when the action button is tapped. See the documentation for details: https://docs.ntfy.sh/publish/#send-http-request" - }, - "actions": { "name": "Action buttons", "description": "Up to three actions ('view', 'broadcast', or 'http') can be added as buttons below the notification. Actions are executed when the corresponding button is tapped or clicked." @@ -374,13 +361,6 @@ "5": "Maximum" } }, - "action_type": { - "options": { - "view": "Open website/app", - "broadcast": "Send Android broadcast", - "http": "Send HTTP request" - } - }, "actions": { "fields": { "label": "Label of the action button", From ddb5d8257a5aa81783e6dab4d6c74f9434c9dba8 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Mon, 15 Sep 2025 20:56:56 +0000 Subject: [PATCH 7/9] empty From 6e5ea86d32c937cdfca4d8158a546f717ad93352 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:37:45 +0000 Subject: [PATCH 8/9] strings --- homeassistant/components/ntfy/strings.json | 2 +- tests/components/ntfy/test_services.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ntfy/strings.json b/homeassistant/components/ntfy/strings.json index feeb8e319c3c8..b9f89e7126e53 100644 --- a/homeassistant/components/ntfy/strings.json +++ b/homeassistant/components/ntfy/strings.json @@ -292,7 +292,7 @@ "message": "Delayed call notifications are not supported" }, "too_many_actions": { - "message": "Too many actions defined. Only 3 allowed" + "message": "Too many actions defined. A maximum of 3 is supported" } }, "services": { diff --git a/tests/components/ntfy/test_services.py b/tests/components/ntfy/test_services.py index b8859193d5f21..af63522c6883f 100644 --- a/tests/components/ntfy/test_services.py +++ b/tests/components/ntfy/test_services.py @@ -196,7 +196,7 @@ async def test_send_message_exception( {"action": "broadcast", "label": "4"}, ], }, - "Too many actions defined. Only 3 allowed", + "Too many actions defined. A maximum of 3 is supported", ), ], ) From c753e13033d0d33fd9eea1196fb6e8eb160a6f7a Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:35:04 +0000 Subject: [PATCH 9/9] fix prettier sorting --- homeassistant/components/ntfy/icons.json | 84 ++-- homeassistant/components/ntfy/strings.json | 474 ++++++++++----------- 2 files changed, 279 insertions(+), 279 deletions(-) diff --git a/homeassistant/components/ntfy/icons.json b/homeassistant/components/ntfy/icons.json index 1f2ab5ef5f6d0..5dcf82ac3b5ef 100644 --- a/homeassistant/components/ntfy/icons.json +++ b/homeassistant/components/ntfy/icons.json @@ -1,72 +1,72 @@ { "entity": { - "notify": { - "publish": { - "default": "mdi:console-line" - } - }, "event": { "subscribe": { "default": "mdi:message-outline" } }, + "notify": { + "publish": { + "default": "mdi:console-line" + } + }, "sensor": { - "messages": { - "default": "mdi:message-arrow-right-outline" - }, - "messages_remaining": { - "default": "mdi:message-plus-outline" + "attachment_bandwidth": { + "default": "mdi:cloud-upload" }, - "messages_limit": { - "default": "mdi:message-alert-outline" + "attachment_expiry_duration": { + "default": "mdi:cloud-clock" }, - "messages_expiry_duration": { - "default": "mdi:message-text-clock" + "attachment_file_size": { + "default": "mdi:file-alert" }, - "emails": { - "default": "mdi:email-arrow-right-outline" + "attachment_total_size": { + "default": "mdi:database-arrow-right" }, - "emails_remaining": { - "default": "mdi:email-plus-outline" + "attachment_total_size_limit": { + "default": "mdi:database-alert" }, - "emails_limit": { - "default": "mdi:email-alert-outline" + "attachment_total_size_remaining": { + "default": "mdi:database-plus" }, "calls": { "default": "mdi:phone-outgoing" }, + "calls_limit": { + "default": "mdi:phone-alert" + }, "calls_remaining": { "default": "mdi:phone-plus" }, - "calls_limit": { - "default": "mdi:phone-alert" + "emails": { + "default": "mdi:email-arrow-right-outline" }, - "reservations": { - "default": "mdi:lock" + "emails_limit": { + "default": "mdi:email-alert-outline" }, - "reservations_remaining": { - "default": "mdi:lock-plus" + "emails_remaining": { + "default": "mdi:email-plus-outline" }, - "reservations_limit": { - "default": "mdi:lock-alert" + "messages": { + "default": "mdi:message-arrow-right-outline" }, - "attachment_total_size": { - "default": "mdi:database-arrow-right" + "messages_expiry_duration": { + "default": "mdi:message-text-clock" }, - "attachment_total_size_remaining": { - "default": "mdi:database-plus" + "messages_limit": { + "default": "mdi:message-alert-outline" }, - "attachment_total_size_limit": { - "default": "mdi:database-alert" + "messages_remaining": { + "default": "mdi:message-plus-outline" }, - "attachment_expiry_duration": { - "default": "mdi:cloud-clock" + "reservations": { + "default": "mdi:lock" }, - "attachment_file_size": { - "default": "mdi:file-alert" + "reservations_limit": { + "default": "mdi:lock-alert" }, - "attachment_bandwidth": { - "default": "mdi:cloud-upload" + "reservations_remaining": { + "default": "mdi:lock-plus" }, "tier": { "default": "mdi:star" @@ -75,10 +75,10 @@ }, "services": { "publish": { - "service": "mdi:send", "sections": { "actions": "mdi:gesture-tap-button" - } + }, + "service": "mdi:send" } } } diff --git a/homeassistant/components/ntfy/strings.json b/homeassistant/components/ntfy/strings.json index b9f89e7126e53..7f24229ca0503 100644 --- a/homeassistant/components/ntfy/strings.json +++ b/homeassistant/components/ntfy/strings.json @@ -1,38 +1,22 @@ { "common": { - "topic": "Topic", - "add_topic_description": "Set up a topic for notifications." + "add_topic_description": "Set up a topic for notifications.", + "topic": "Topic" }, "config": { + "abort": { + "account_mismatch": "The provided access token corresponds to the account {wrong_username}. Please re-authenticate with the account **{username}**", + "already_configured": "[%key:common::config_flow::abort::already_configured_service%]", + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", + "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]" + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, "step": { - "user": { - "description": "Set up **ntfy** push notification service", - "data": { - "url": "Service URL", - "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" - }, - "data_description": { - "url": "Address of the ntfy service. Modify this if you want to use a different server", - "verify_ssl": "Enable SSL certificate verification for secure connections. Disable only if connecting to a ntfy instance using a self-signed certificate" - }, - "sections": { - "auth": { - "name": "Authentication", - "description": "Depending on whether the server is configured to support access control, some topics may be read/write protected so that only users with the correct credentials can subscribe or publish to them. To publish/subscribe to protected topics, you can provide a username and password. Home Assistant will automatically generate an access token to authenticate with ntfy.", - "data": { - "username": "[%key:common::config_flow::data::username%]", - "password": "[%key:common::config_flow::data::password%]" - }, - "data_description": { - "username": "Enter the username required to authenticate with protected ntfy topics", - "password": "Enter the password corresponding to the provided username for authentication" - } - } - } - }, "reauth_confirm": { - "title": "Re-authenticate with ntfy ({name})", - "description": "The access token for **{username}** is invalid. To re-authenticate with the ntfy service, you can either log in with your password (a new access token will be created automatically) or you can directly provide a valid access token", "data": { "password": "[%key:common::config_flow::data::password%]", "token": "[%key:common::config_flow::data::access_token%]" @@ -40,25 +24,25 @@ "data_description": { "password": "Enter the password corresponding to the aforementioned username to automatically create an access token", "token": "Enter a new access token. To create a new access token navigate to Account → Access tokens and select 'Create access token'" - } + }, + "description": "The access token for **{username}** is invalid. To re-authenticate with the ntfy service, you can either log in with your password (a new access token will be created automatically) or you can directly provide a valid access token", + "title": "Re-authenticate with ntfy ({name})" }, "reconfigure": { - "title": "Configuration for {name}", - "description": "You can either log in with your **ntfy** username and password, and Home Assistant will automatically create an access token to authenticate with **ntfy**, or you can provide an access token directly", "data": { - "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", - "token": "[%key:common::config_flow::data::access_token%]" + "token": "[%key:common::config_flow::data::access_token%]", + "username": "[%key:common::config_flow::data::username%]" }, "data_description": { - "username": "[%key:component::ntfy::config::step::user::sections::auth::data_description::username%]", "password": "[%key:component::ntfy::config::step::user::sections::auth::data_description::password%]", - "token": "Enter a new or existing access token. To create a new access token navigate to Account → Access tokens and select 'Create access token'" - } + "token": "Enter a new or existing access token. To create a new access token navigate to Account → Access tokens and select 'Create access token'", + "username": "[%key:component::ntfy::config::step::user::sections::auth::data_description::username%]" + }, + "description": "You can either log in with your **ntfy** username and password, and Home Assistant will automatically create an access token to authenticate with **ntfy**, or you can provide an access token directly", + "title": "Configuration for {name}" }, "reconfigure_user": { - "title": "[%key:component::ntfy::config::step::reconfigure::title%]", - "description": "Enter the password for **{username}** below. Home Assistant will automatically create a new access token to authenticate with **ntfy**. You can also directly provide a valid access token", "data": { "password": "[%key:common::config_flow::data::password%]", "token": "[%key:common::config_flow::data::access_token%]" @@ -66,92 +50,108 @@ "data_description": { "password": "[%key:component::ntfy::config::step::reauth_confirm::data_description::password%]", "token": "[%key:component::ntfy::config::step::reconfigure::data_description::token%]" + }, + "description": "Enter the password for **{username}** below. Home Assistant will automatically create a new access token to authenticate with **ntfy**. You can also directly provide a valid access token", + "title": "[%key:component::ntfy::config::step::reconfigure::title%]" + }, + "user": { + "data": { + "url": "Service URL", + "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" + }, + "data_description": { + "url": "Address of the ntfy service. Modify this if you want to use a different server", + "verify_ssl": "Enable SSL certificate verification for secure connections. Disable only if connecting to a ntfy instance using a self-signed certificate" + }, + "description": "Set up **ntfy** push notification service", + "sections": { + "auth": { + "data": { + "password": "[%key:common::config_flow::data::password%]", + "username": "[%key:common::config_flow::data::username%]" + }, + "data_description": { + "password": "Enter the password corresponding to the provided username for authentication", + "username": "Enter the username required to authenticate with protected ntfy topics" + }, + "description": "Depending on whether the server is configured to support access control, some topics may be read/write protected so that only users with the correct credentials can subscribe or publish to them. To publish/subscribe to protected topics, you can provide a username and password. Home Assistant will automatically generate an access token to authenticate with ntfy.", + "name": "Authentication" + } } } - }, - "error": { - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", - "unknown": "[%key:common::config_flow::error::unknown%]" - }, - "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_service%]", - "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", - "account_mismatch": "The provided access token corresponds to the account {wrong_username}. Please re-authenticate with the account **{username}**", - "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]" } }, "config_subentries": { "topic": { + "abort": { + "already_configured": "Topic is already configured", + "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]" + }, + "entry_type": "[%key:component::ntfy::common::topic%]", + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_topic": "Invalid topic. Only letters, numbers, underscores, or dashes allowed.", + "publish_forbidden": "Publishing to this topic is forbidden", + "unknown": "[%key:common::config_flow::error::unknown%]" + }, + "initiate_flow": { + "user": "Add topic" + }, "step": { - "user": { - "title": "[%key:component::ntfy::common::topic%]", - "description": "[%key:component::ntfy::common::add_topic_description%]", - "menu_options": { - "add_topic": "Enter topic", - "generate_topic": "Generate topic name" - } - }, "add_topic": { - "title": "[%key:component::ntfy::common::topic%]", - "description": "[%key:component::ntfy::common::add_topic_description%]", "data": { - "topic": "[%key:component::ntfy::common::topic%]", - "name": "Display name" + "name": "Display name", + "topic": "[%key:component::ntfy::common::topic%]" }, "data_description": { - "topic": "Enter the name of the topic you want to use for notifications. Topics may not be password-protected, so choose a name that's not easy to guess.", - "name": "Set an alternative name to display instead of the topic name. This helps identify topics with complex or hard-to-read names more easily." + "name": "Set an alternative name to display instead of the topic name. This helps identify topics with complex or hard-to-read names more easily.", + "topic": "Enter the name of the topic you want to use for notifications. Topics may not be password-protected, so choose a name that's not easy to guess." }, + "description": "[%key:component::ntfy::common::add_topic_description%]", "sections": { "filter": { "data": { + "filter_message": "Filter by message content", "filter_priority": "Filter by priority", "filter_tags": "Filter by tags", - "filter_title": "Filter by title", - "filter_message": "Filter by message content" + "filter_title": "Filter by title" }, "data_description": { + "filter_message": "Include messages with content that exactly matches the specified text", "filter_priority": "Include messages that match any of the selected priority levels. If no priority is selected, all messages are included by default", "filter_tags": "Only include messages that have all selected tags", - "filter_title": "Include messages with a title that exactly matches the specified text", - "filter_message": "Include messages with content that exactly matches the specified text" + "filter_title": "Include messages with a title that exactly matches the specified text" }, - "name": "Message filters (optional)", - "description": "Apply filters to narrow down the messages received when Home Assistant subscribes to the topic. Filters apply only to the event entity." + "description": "Apply filters to narrow down the messages received when Home Assistant subscribes to the topic. Filters apply only to the event entity.", + "name": "Message filters (optional)" } - } + }, + "title": "[%key:component::ntfy::common::topic%]" }, "reconfigure": { - "title": "Message filters for {topic}", - "description": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::description%]", "data": { + "filter_message": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data::filter_message%]", "filter_priority": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data::filter_priority%]", "filter_tags": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data::filter_tags%]", - "filter_title": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data::filter_title%]", - "filter_message": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data::filter_message%]" + "filter_title": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data::filter_title%]" }, "data_description": { + "filter_message": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data_description::filter_message%]", "filter_priority": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data_description::filter_priority%]", "filter_tags": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data_description::filter_tags%]", - "filter_title": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data_description::filter_title%]", - "filter_message": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data_description::filter_message%]" - } + "filter_title": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::data_description::filter_title%]" + }, + "description": "[%key:component::ntfy::config_subentries::topic::step::add_topic::sections::filter::description%]", + "title": "Message filters for {topic}" + }, + "user": { + "description": "[%key:component::ntfy::common::add_topic_description%]", + "menu_options": { + "add_topic": "Enter topic", + "generate_topic": "Generate topic name" + }, + "title": "[%key:component::ntfy::common::topic%]" } - }, - "initiate_flow": { - "user": "Add topic" - }, - "entry_type": "[%key:component::ntfy::common::topic%]", - "error": { - "publish_forbidden": "Publishing to this topic is forbidden", - "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", - "unknown": "[%key:common::config_flow::error::unknown%]", - "invalid_topic": "Invalid topic. Only letters, numbers, underscores, or dashes allowed." - }, - "abort": { - "already_configured": "Topic is already configured", - "reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]" } } }, @@ -159,125 +159,118 @@ "event": { "subscribe": { "state_attributes": { + "actions": { "name": "Actions" }, + "attachment": { "name": "Attachment" }, + "click": { "name": "Click" }, "event_type": { "state": { "triggered": "Triggered" } }, - "time": { "name": "Time" }, "expires": { "name": "Expires" }, - "topic": { "name": "[%key:component::ntfy::common::topic%]" }, + "icon": { "name": "Icon" }, "message": { "name": "Message" }, - "title": { "name": "Title" }, - "tags": { "name": "Tags" }, "priority": { "name": "Priority" }, - "click": { "name": "Click" }, - "icon": { "name": "Icon" }, - "actions": { "name": "Actions" }, - "attachment": { "name": "Attachment" } + "tags": { "name": "Tags" }, + "time": { "name": "Time" }, + "title": { "name": "Title" }, + "topic": { "name": "[%key:component::ntfy::common::topic%]" } } } }, "sensor": { - "messages": { - "name": "Messages published", - "unit_of_measurement": "messages" + "attachment_bandwidth": { + "name": "Attachment bandwidth limit" }, - "messages_remaining": { - "name": "Messages remaining", - "unit_of_measurement": "[%key:component::ntfy::entity::sensor::messages::unit_of_measurement%]" + "attachment_expiry_duration": { + "name": "Attachment expiry duration" }, - "messages_limit": { - "name": "Messages usage limit", - "unit_of_measurement": "[%key:component::ntfy::entity::sensor::messages::unit_of_measurement%]" + "attachment_file_size": { + "name": "Attachment file size limit" }, - "messages_expiry_duration": { - "name": "Messages expiry duration" + "attachment_total_size": { + "name": "Attachment storage" + }, + "attachment_total_size_limit": { + "name": "Attachment storage limit" + }, + "attachment_total_size_remaining": { + "name": "Attachment storage remaining" + }, + "calls": { + "name": "Phone calls made", + "unit_of_measurement": "calls" + }, + "calls_limit": { + "name": "Phone calls usage limit", + "unit_of_measurement": "[%key:component::ntfy::entity::sensor::calls::unit_of_measurement%]" + }, + "calls_remaining": { + "name": "Phone calls remaining", + "unit_of_measurement": "[%key:component::ntfy::entity::sensor::calls::unit_of_measurement%]" }, "emails": { "name": "Emails sent", "unit_of_measurement": "emails" }, + "emails_limit": { + "name": "Email usage limit", + "unit_of_measurement": "[%key:component::ntfy::entity::sensor::emails::unit_of_measurement%]" + }, "emails_remaining": { "name": "Emails remaining", "unit_of_measurement": "[%key:component::ntfy::entity::sensor::emails::unit_of_measurement%]" }, - "emails_limit": { - "name": "Email usage limit", - "unit_of_measurement": "[%key:component::ntfy::entity::sensor::emails::unit_of_measurement%]" + "messages": { + "name": "Messages published", + "unit_of_measurement": "messages" }, - "calls": { - "name": "Phone calls made", - "unit_of_measurement": "calls" + "messages_expiry_duration": { + "name": "Messages expiry duration" }, - "calls_remaining": { - "name": "Phone calls remaining", - "unit_of_measurement": "[%key:component::ntfy::entity::sensor::calls::unit_of_measurement%]" + "messages_limit": { + "name": "Messages usage limit", + "unit_of_measurement": "[%key:component::ntfy::entity::sensor::messages::unit_of_measurement%]" }, - "calls_limit": { - "name": "Phone calls usage limit", - "unit_of_measurement": "[%key:component::ntfy::entity::sensor::calls::unit_of_measurement%]" + "messages_remaining": { + "name": "Messages remaining", + "unit_of_measurement": "[%key:component::ntfy::entity::sensor::messages::unit_of_measurement%]" }, "reservations": { "name": "Reserved topics", "unit_of_measurement": "topics" }, - "reservations_remaining": { - "name": "Reserved topics remaining", - "unit_of_measurement": "[%key:component::ntfy::entity::sensor::reservations::unit_of_measurement%]" - }, "reservations_limit": { "name": "Reserved topics limit", "unit_of_measurement": "[%key:component::ntfy::entity::sensor::reservations::unit_of_measurement%]" }, - "attachment_total_size": { - "name": "Attachment storage" - }, - "attachment_total_size_remaining": { - "name": "Attachment storage remaining" - }, - "attachment_total_size_limit": { - "name": "Attachment storage limit" - }, - "attachment_expiry_duration": { - "name": "Attachment expiry duration" - }, - "attachment_file_size": { - "name": "Attachment file size limit" - }, - "attachment_bandwidth": { - "name": "Attachment bandwidth limit" + "reservations_remaining": { + "name": "Reserved topics remaining", + "unit_of_measurement": "[%key:component::ntfy::entity::sensor::reservations::unit_of_measurement%]" }, "tier": { "name": "Subscription tier", "state": { + "business": "Business", "free": "Free", - "supporter": "Supporter", "pro": "Pro", - "business": "Business" + "supporter": "Supporter" } } } }, "exceptions": { - "publish_failed_request_error": { - "message": "Failed to publish notification: {error_msg}" - }, - - "publish_failed_exception": { - "message": "Failed to publish notification due to a connection error" - }, "authentication_error": { "message": "Failed to authenticate with ntfy service. Please verify your credentials" }, - "server_error": { - "message": "Failed to connect to ntfy service due to a server error: {error_msg}" - }, "connection_error": { "message": "Failed to connect to ntfy service due to a connection error" }, - "timeout_error": { - "message": "Failed to connect to ntfy service due to a connection timeout" + "delay_no_call": { + "message": "Delayed call notifications are not supported" + }, + "delay_no_email": { + "message": "Delayed email notifications are not supported" }, "entity_not_found": { "message": "The selected ntfy entity could not be found." @@ -285,107 +278,114 @@ "entry_not_loaded": { "message": "The selected ntfy service is currently not loaded or disabled in Home Assistant." }, - "delay_no_email": { - "message": "Delayed email notifications are not supported" + "publish_failed_exception": { + "message": "Failed to publish notification due to a connection error" }, - "delay_no_call": { - "message": "Delayed call notifications are not supported" + "publish_failed_request_error": { + "message": "Failed to publish notification: {error_msg}" + }, + + "server_error": { + "message": "Failed to connect to ntfy service due to a server error: {error_msg}" + }, + "timeout_error": { + "message": "Failed to connect to ntfy service due to a connection timeout" }, "too_many_actions": { "message": "Too many actions defined. A maximum of 3 is supported" } }, + "issues": { + "topic_protected": { + "fix_flow": { + "step": { + "confirm": { + "description": "The topic **{topic}** is protected and requires authentication to subscribe.\n\nTo resolve this issue, you have two options:\n\n1. **Reconfigure the ntfy integration**\nAdd a username and password that has permission to access this topic.\n\n2. **Deactivate the event entity**\nThis will stop Home Assistant from subscribing to the topic.\nClick **Submit** to deactivate the entity.", + "title": "Topic {topic} is protected" + } + } + }, + "title": "Subscription failed: Topic {topic} is protected" + } + }, + "selector": { + "actions": { + "fields": { + "action": "Action type", + "body": "Body of the HTTP request ('http' action only)", + "clear": "Clear notification after action button is tapped", + "extras": "Extras to include in the intent as key-value pairs ('broadcast' action only)", + "headers": "Additional HTTP headers as key-value pairs ('http' action only)", + "intent": "Android intent to send when the 'broadcast' action is triggered", + "label": "Label of the action button", + "method": "HTTP method to use for the 'http' action", + "url": "URL to open for the 'view' action or to request for the 'http' action" + } + }, + "priority": { + "options": { + "1": "Minimum", + "2": "[%key:common::state::low%]", + "3": "Default", + "4": "[%key:common::state::high%]", + "5": "Maximum" + } + } + }, "services": { "publish": { - "name": "Publish notification", "description": "Publishes a notification message to a ntfy topic", "fields": { - "title": { - "name": "[%key:component::notify::services::send_message::fields::title::name%]", - "description": "[%key:component::notify::services::send_message::fields::title::description%]" - }, - "message": { - "name": "[%key:component::notify::services::send_message::fields::message::name%]", - "description": "[%key:component::notify::services::send_message::fields::message::description%]" - }, - "markdown": { - "name": "Format as Markdown", - "description": "Enable Markdown formatting for the message body. See the Markdown guide for syntax details: https://www.markdownguide.org/basic-syntax/." + "actions": { + "description": "Up to three actions ('view', 'broadcast', or 'http') can be added as buttons below the notification. Actions are executed when the corresponding button is tapped or clicked.", + "name": "Action buttons" }, - "tags": { - "name": "Tags/Emojis", - "description": "Add tags or emojis to the notification. Emojis (using shortcodes like smile) will appear in the notification title or message. Other tags will be displayed below the notification content." + "attach": { + "description": "Attach images or other files by URL.", + "name": "Attachment URL" }, - "priority": { - "name": "Message priority", - "description": "All messages have a priority that defines how urgently your phone notifies you, depending on the configured vibration patterns, notification sounds, and visibility in the notification drawer or pop-over." + "call": { + "description": "Phone number to call and read the message out loud using text-to-speech. Requires ntfy Pro and prior phone number verification.", + "name": "Phone call" }, "click": { - "name": "Click URL", - "description": "URL that is opened when notification is clicked." + "description": "URL that is opened when notification is clicked.", + "name": "Click URL" }, "delay": { - "name": "Delay delivery", - "description": "Set a delay for message delivery. Minimum delay is 10 seconds, maximum is 3 days." - }, - "attach": { - "name": "Attachment URL", - "description": "Attach images or other files by URL." + "description": "Set a delay for message delivery. Minimum delay is 10 seconds, maximum is 3 days.", + "name": "Delay delivery" }, "email": { - "name": "Forward to email", - "description": "Specify the address to forward the notification to, for example mail@example.com" - }, - "call": { - "name": "Phone call", - "description": "Phone number to call and read the message out loud using text-to-speech. Requires ntfy Pro and prior phone number verification." + "description": "Specify the address to forward the notification to, for example mail@example.com", + "name": "Forward to email" }, "icon": { - "name": "Icon URL", - "description": "Include an icon that will appear next to the text of the notification. Only JPEG and PNG images are supported." + "description": "Include an icon that will appear next to the text of the notification. Only JPEG and PNG images are supported.", + "name": "Icon URL" }, - "actions": { - "name": "Action buttons", - "description": "Up to three actions ('view', 'broadcast', or 'http') can be added as buttons below the notification. Actions are executed when the corresponding button is tapped or clicked." - } - } - } - }, - "selector": { - "priority": { - "options": { - "1": "Minimum", - "2": "[%key:common::state::low%]", - "3": "Default", - "4": "[%key:common::state::high%]", - "5": "Maximum" - } - }, - "actions": { - "fields": { - "label": "Label of the action button", - "action": "Action type", - "url": "URL to open for the 'view' action or to request for the 'http' action", - "intent": "Android intent to send when the 'broadcast' action is triggered", - "extras": "Extras to include in the intent as key-value pairs ('broadcast' action only)", - "method": "HTTP method to use for the 'http' action", - "headers": "Additional HTTP headers as key-value pairs ('http' action only)", - "body": "Body of the HTTP request ('http' action only)", - "clear": "Clear notification after action button is tapped" - } - } - }, - "issues": { - "topic_protected": { - "title": "Subscription failed: Topic {topic} is protected", - "fix_flow": { - "step": { - "confirm": { - "title": "Topic {topic} is protected", - "description": "The topic **{topic}** is protected and requires authentication to subscribe.\n\nTo resolve this issue, you have two options:\n\n1. **Reconfigure the ntfy integration**\nAdd a username and password that has permission to access this topic.\n\n2. **Deactivate the event entity**\nThis will stop Home Assistant from subscribing to the topic.\nClick **Submit** to deactivate the entity." - } + "markdown": { + "description": "Enable Markdown formatting for the message body. See the Markdown guide for syntax details: https://www.markdownguide.org/basic-syntax/.", + "name": "Format as Markdown" + }, + "message": { + "description": "[%key:component::notify::services::send_message::fields::message::description%]", + "name": "[%key:component::notify::services::send_message::fields::message::name%]" + }, + "priority": { + "description": "All messages have a priority that defines how urgently your phone notifies you, depending on the configured vibration patterns, notification sounds, and visibility in the notification drawer or pop-over.", + "name": "Message priority" + }, + "tags": { + "description": "Add tags or emojis to the notification. Emojis (using shortcodes like smile) will appear in the notification title or message. Other tags will be displayed below the notification content.", + "name": "Tags/Emojis" + }, + "title": { + "description": "[%key:component::notify::services::send_message::fields::title::description%]", + "name": "[%key:component::notify::services::send_message::fields::title::name%]" } - } + }, + "name": "Publish notification" } } }