Skip to content

Commit 9d5dbc4

Browse files
authored
[ntfy] Initial contribution (#20560)
* ntfy Binding Signed-off-by: Christian Kittel <ckittel@gmx.de>
1 parent 8beefd5 commit 9d5dbc4

37 files changed

Lines changed: 3603 additions & 0 deletions

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@
284284
/bundles/org.openhab.binding.nikohomecontrol/ @mherwege
285285
/bundles/org.openhab.binding.nobohub/ @espenaf
286286
/bundles/org.openhab.binding.novafinedust/ @t2000
287+
/bundles/org.openhab.binding.ntfy/ @EvilPingu
287288
/bundles/org.openhab.binding.ntp/ @marcelrv
288289
/bundles/org.openhab.binding.nuki/ @janvyb
289290
/bundles/org.openhab.binding.nuvo/ @mlobstein

bom/openhab-addons/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,11 @@
14011401
<artifactId>org.openhab.binding.novafinedust</artifactId>
14021402
<version>${project.version}</version>
14031403
</dependency>
1404+
<dependency>
1405+
<groupId>org.openhab.addons.bundles</groupId>
1406+
<artifactId>org.openhab.binding.ntfy</artifactId>
1407+
<version>${project.version}</version>
1408+
</dependency>
14041409
<dependency>
14051410
<groupId>org.openhab.addons.bundles</groupId>
14061411
<artifactId>org.openhab.binding.ntp</artifactId>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Ntfy Binding
2+
3+
The Ntfy binding enables openHAB to publish notifications to Ntfy-compatible servers (for example [ntfy.sh](https://ntfy.sh) or a self-hosted ntfy-compatible endpoint).
4+
5+
Ntfy is a simple HTTP-based notification service and message broker; see [ntfy.sh](https://ntfy.sh) for details and public servers.
6+
7+
It is intended for integrations where openHAB should push alert or informational messages to mobile or desktop clients that support the Ntfy protocol.
8+
The binding supports basic text messages as well as common rich features supported by the protocol: message priority, tags, icon URLs, attachments, click actions and simple action buttons
9+
10+
Typical uses include doorbell alerts, security notifications, system health messages, or any automation that should notify users in real time via an Ntfy-compatible notification channel.
11+
12+
## Supported Things
13+
14+
This binding provides the following Thing types:
15+
16+
- `server` (bridge) — Represents a connection to an Ntfy server. The bridge holds shared connection settings (hostname, username, password, connectionTimeout) which are used by topic Things.
17+
- `ntfy-topic` (Thing) — Represents a topic/channel on a configured Ntfy server. Each topic Thing must be associated with a `server` bridge.
18+
19+
## Thing Configuration
20+
21+
### `server` Bridge Configuration
22+
23+
| Name | Type | Description | Default | Required | Advanced |
24+
|-------------------|---------|------------------------------------------------------------------------------------------|------------------------------------|----------|----------|
25+
| hostname | text | Base URL of the ntfy server | [https://ntfy.sh](https://ntfy.sh) | yes | no |
26+
| username | text | Optional username for basic auth | N/A | no | no |
27+
| password | text | Optional password - if username is provided basic auth is used else Bearer token is used | N/A | no | no |
28+
| connectionTimeout | integer | WebSocket / HTTP connection timeout ms | 60000 | no | yes |
29+
30+
Configure the `server` as a bridge to hold shared server and authentication settings.
31+
For authentication with access token only set the password and leave the username empty.
32+
33+
### `ntfy-topic` Thing Configuration
34+
35+
| Name | Type | Description | Default | Required | Advanced |
36+
|-----------|------|---------------------------|---------|----------|----------|
37+
| topicName | text | Name of the topic/channel | N/A | yes | no |
38+
39+
## Channels
40+
41+
### `ntfy-topic` Channels
42+
43+
| Channel | Type | Read/Write | Description |
44+
|-------------------|----------|------------|---------------------------------------------|
45+
| last-message | String | R | Last received message payload (read-only) |
46+
| last-message-time | DateTime | R | Timestamp of the last message (read-only) |
47+
| last-message-id | String | R | ID of the last message (read-only) |
48+
49+
## Full Example
50+
51+
Below are examples for textual configuration files showing a bridge (`server`), a topic Thing (`ntfy-topic`), Items bound to the Thing's channels, and a simple sitemap to display the last message and its timestamp.
52+
53+
### Thing Configuration
54+
55+
```java
56+
Bridge ntfy:server:myConn "Ntfy Server" [ hostname="https://ntfy.sh", connectionTimeout=60000, username="ntfyUser", password="MyPaSsWoRd"]
57+
58+
Thing ntfy:ntfy-topic:home "Front Door Notifications" (ntfy:server:myConn) [ topicName="home" ]
59+
```
60+
61+
### Item Configuration (items file)
62+
63+
Bind Items to the read-only channels exposed by the topic Thing to show the last received message and its timestamp:
64+
65+
```java
66+
String FrontDoorLastMessage "Front Door Message" { channel="ntfy:ntfy-topic:home:last-message" }
67+
DateTime FrontDoorMessageTime "Last message received" { channel="ntfy:ntfy-topic:home:last-message-time" }
68+
```
69+
70+
Note: Sending notifications from openHAB to ntfy is done via the binding's Actions from rules (the binding exposes Actions to publish/delete messages). The channels above are read-only and show incoming or last-state values.
71+
72+
### Sitemap Configuration (sitemap file)
73+
74+
Simple sitemap demonstrating how to display the last message and its timestamp:
75+
76+
```perl
77+
sitemap notifications label="Notifications"
78+
{
79+
<Frame label="Front Door">
80+
Text item=FrontDoorLastMessage label="Last message [%s]"
81+
Text item=FrontDoorMessageTime label="Received [%1$td.%1$tm.%1$tY %1$tR]"
82+
</Frame>
83+
}
84+
```
85+
86+
## Actions (Rules DSL and JavaScript)
87+
88+
This binding exposes Rule Actions to send and manage messages on a configured topic.
89+
Actions are available for use from the Rules DSL and the ECMAScript/JavaScript automation scripts.
90+
The actions support a builder-style API to configure a message (message text, priority, tags, icon, attachments, actions, sequence id, ...) and then send it.
91+
92+
Important: Always check that the Thing exist before invoking methods.
93+
94+
### Available action methods
95+
96+
Below is a quick reference of the builder-style methods provided by the binding actions. Use these to construct messages before calling `send()`.
97+
98+
| Method | Parameters | Description | Ntfy docs |
99+
|-------------------------|---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------------|
100+
| withMessage | (String message) | Set the main message text. | [publish](https://docs.ntfy.sh/publish) |
101+
| withPriority | (int priority) | Set message priority (1-5). | [priority](https://ntfy.sh/docs/publish/#priority) |
102+
| withTitle | (String title) | Set title. | [title](https://docs.ntfy.sh/publish/#message-title) |
103+
| withTag | (String tag) | Add a tag to the message. | [tags](https://ntfy.sh/docs/publish/#tags) |
104+
| withIcon | (String url) | Set an icon URL for the notification. | [icons](https://ntfy.sh/docs/publish/#icons) |
105+
| withAttachment | (String url, String filename) | Attach a file or resource URL with optional filename. | [attachments](https://docs.ntfy.sh/publish/#attach-file-from-a-url) |
106+
| withViewAction | (String label, Boolean clearNotification, String url) | Add a view action (opens URL) with optional clear flag. | [actions](https://docs.ntfy.sh/publish/#open-websiteapp) |
107+
| withCopyAction | (String label, Boolean clearNotification, String value) | Add an action that copies text to clipboard on the client. | [actions](https://docs.ntfy.sh/publish/#copy-to-clipboard) |
108+
| withHttpAction | (String label, Boolean clearNotification, String url, String method, String headers, String body) | Add an HTTP action with optional method, headers and body. | [actions](https://docs.ntfy.sh/publish/#send-http-request) |
109+
| withBroadcastAction | (String label, Boolean clearNotification, String params) | Add a broadcast action to trigger local apps/handlers. | [actions](https://docs.ntfy.sh/publish/#send-android-broadcast) |
110+
| withDelay | (String delay) | Assign a delay. | [delay](https://docs.ntfy.sh/publish/#scheduled-delivery) |
111+
| withSequenceId | (String sequenceId) | Assign a sequence id for later reference (useful for delete/update). | [sequence id](https://docs.ntfy.sh/publish/#updating-deleting-notifications) |
112+
| send | () | Send the built message; returns a message ID string. | [publish](https://ntfy.sh/docs/publish/) |
113+
| send | (String file, String filename, String sequenceId) | Send a file; returns a message ID string. | [publish_local_file](https://docs.ntfy.sh/publish/#attach-local-file) |
114+
| delete | (String sequenceId) | Delete a message previously sent with the given sequence id. | [delete](https://ntfy.sh/docs/publish/#deleting-notifications) |
115+
| clearNtfyMessageBuilder | () | Reset the message build explicitly | |
116+
117+
For more information about ntfy features and the notification format, see the ntfy project documentation: [https://ntfy.sh/docs/](https://ntfy.sh/docs/)
118+
119+
### Rules DSL example
120+
121+
```rules
122+
// Obtain the Thing-specific actions and use the builder to send a message
123+
val bindingActions = getActions("ntfy", "ntfy:ntfy-topic:frontdoor")
124+
if (bindingActions !== null) {
125+
// simple one-liner: send a message
126+
val msgId = bindingActions.withMessage("Someone is at the door").withPriority(4).send()
127+
128+
// or build up a more complex message
129+
bindingActions.withMessage("Doorbell pressed")
130+
.withSequenceId("doorbell-1")
131+
.withTag("door")
132+
.withIcon("https://example.org/icons/door.png")
133+
.withViewAction("Open UI", true, "https://example.org/ui")
134+
.send()
135+
136+
// delete a previously sent message by sequence id
137+
bindingActions.delete("doorbell-1")
138+
}
139+
```
140+
141+
The builder methods available include: withMessage(String), withPriority(int), withTag(String), withIcon(String), withAttachment(String, String), withViewAction(...), withCopyAction(...), withHttpAction(...), withBroadcastAction(...), withSequenceId(String), send(), and delete(String).
142+
143+
### ECMAScript / JavaScript example
144+
145+
In ECMAScript scripts you can obtain the Thing Actions and use the same builder API. The example below assumes the automation engine provides an `actions` helper (typical in openHAB JavaScript environment):
146+
147+
```javascript
148+
// get the Thing actions for the topic Thing
149+
var bindingActions = actions.get("ntfy", "ntfy:ntfy-topic:frontdoor");
150+
if (bindingActions) {
151+
// send a simple message
152+
var id = bindingActions.withMessage("Garage opened").withPriority(3).send();
153+
154+
// build and send a message with extra features
155+
bindingActions.withMessage("Motion detected in garage")
156+
.withSequenceId("garage-1")
157+
.withTag("motion")
158+
.withHttpAction("Open Camera", true, "https://camera/local/snap", "POST", null, null)
159+
.send();
160+
}
161+
```
162+
163+
If your scripting environment exposes a different API to retrieve Thing Actions, adapt the call accordingly. The important part is to obtain the Thing-specific actions object for the binding and then use the builder methods shown above.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.openhab.addons.bundles</groupId>
9+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
10+
<version>5.2.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>org.openhab.binding.ntfy</artifactId>
14+
15+
<name>openHAB Add-ons :: Bundles :: Ntfy Binding</name>
16+
17+
</project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<features name="org.openhab.binding.ntfy-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.6.0">
3+
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
4+
5+
<feature name="openhab-binding-ntfy" description="Ntfy Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ntfy/${project.version}</bundle>
8+
</feature>
9+
</features>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.ntfy.internal;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.core.thing.ThingTypeUID;
17+
18+
/**
19+
* The {@link NtfyBindingConstants} class defines common constants, which are
20+
* used across the whole binding.
21+
*
22+
* @author Christian Kittel - Initial contribution
23+
*/
24+
@NonNullByDefault
25+
public class NtfyBindingConstants {
26+
27+
public static final String BINDING_ID = "ntfy";
28+
29+
// List of all Thing Type UIDs
30+
public static final ThingTypeUID NTFY_CONNECTION_THING = new ThingTypeUID(BINDING_ID, "server");
31+
public static final ThingTypeUID NTFY_TOPIC_THING = new ThingTypeUID(BINDING_ID, "ntfy-topic");
32+
33+
// List of all Channel ids
34+
public static final String CHANNEL_LASTMESSAGE = "last-message";
35+
public static final String CHANNEL_LASTMESSAGETIME = "last-message-time";
36+
public static final String CHANNEL_LASTMESSAGEID = "last-message-id";
37+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2010-2026 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.ntfy.internal;
14+
15+
import java.nio.charset.StandardCharsets;
16+
import java.util.Base64;
17+
import java.util.Objects;
18+
19+
import org.eclipse.jdt.annotation.NonNullByDefault;
20+
import org.eclipse.jdt.annotation.Nullable;
21+
22+
/**
23+
* The {@link NtfyConnectionConfiguration} class contains fields mapping thing configuration parameters.
24+
*
25+
* @author Christian Kittel - Initial contribution
26+
*/
27+
@NonNullByDefault
28+
public class NtfyConnectionConfiguration {
29+
30+
/**
31+
* Hostname of the ntfy server, e.g. "https://ntfy.sh"
32+
*/
33+
public String hostname = "";
34+
35+
/**
36+
* Username for basic authentication
37+
*/
38+
public @Nullable String username;
39+
40+
/**
41+
* Password for basic authentication
42+
*/
43+
public @Nullable String password;
44+
45+
/**
46+
* Connection timeout in milliseconds
47+
*/
48+
public long connectionTimeout = 60000;
49+
50+
/**
51+
* Checks whether a Basic Authorization header should be provided based on the configured password value.
52+
*
53+
* @return {@code true} when password are non-null and non-blank,
54+
* {@code false} otherwise
55+
*/
56+
public boolean isAuthHeaderNeeded() {
57+
final @Nullable String password = this.password;
58+
return password != null && !password.isBlank();
59+
}
60+
61+
/**
62+
* If both username and password are provided, a Basic Authorization header is created. If only password is
63+
* provided, a Bearer Authorization header is created
64+
*
65+
* @return the full value for the HTTP Authorization header
66+
*/
67+
public String buildAuthHeader() {
68+
final @Nullable String username = this.username;
69+
final String password = Objects.requireNonNull(this.password);
70+
if (username == null || username.isBlank()) {
71+
return "Bearer " + password;
72+
}
73+
return "Basic "
74+
+ Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
75+
}
76+
}

0 commit comments

Comments
 (0)