ntfy (pronounce: notify) is a simple HTTP-based pub-sub notification service. It allows you to send notifications to your phone or desktop via scripts from any computer, entirely without signup, cost or setup. It’s also open source if you want to run your own. Visit ntfy.sh for more details.
{ntfy} is a lightweight R wrapper for this service. The magic sauce is just POST
and GET
calls equivalent to
but made to work nicely in an R workflow.
Installation
You can install the released version of {ntfy} from CRAN
install.packages("ntfy")
You can install the development version of {ntfy} from GitHub:
# install.packages("remotes")
remotes::install_github("jonocarroll/ntfy")
Functionality
Follow the instructions at ntfy.sh to install any of the mobile apps or use the web app. No sign-up or account is necessary.
Choose a topic (note: this isn’t a password-protected service, so choose something obscure) and subscribe to it on your device.
Add the topic as an environment variable, e.g.
The server will automatically be set to https://ntfy.sh unless you specify another.
This can be confirmed with
ntfy_server()
#> [1] "https://ntfy.sh"
With the package loaded, you can now send notifications which should appear on your device
This returns the {httr2} response object invisibly, or errors if it was unsuccessful.
{ntfy} can be used in many ways. One would be to notify the completion of a process. The ntfy_done()
function sends a notification with the (default) body
slow_process <- function(x) {
Sys.sleep(8) # sleep for 8 seconds
x
}
mtcars |>
head() |>
slow_process() |>
ntfy_done()
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
which results in a notification on subscribed devices
When using the base R pipe |>
the piped commands are composed together by the parser, so
becomes
We can use this fact to time the running of a process if the last function (above, h()
) is system.time()
. The ntfy_done_with_timing()
function does exactly this
mtcars |>
head() |>
slow_process() |>
ntfy_done_with_timing()
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
which sends the notification
This service can also be used as a progress indicator via the {progressr} package - see help("handler_ntfy", package = "progressr")
or https://progressr.futureverse.org/reference/handler_ntfy.html for more details.
If you’re using a topic on a server that requires authentication, you can pass auth = TRUE
, along with a username and password:
ntfy_send(
"test from R!",
auth = TRUE,
username = "example",
password = "super-secret-password"
)
Alternatively, you can set these as environment variables and they’ll get used by ntfy_send()
automatically:
usethis::edit_r_environ()
[...]
NTFY_AUTH='TRUE'
NTFY_USERNAME='example'
NTFY_PASSWORD='super-secret-password'
ntfy_send("test from R!")
The history of the notifications sent can be retrieved as well, with control over how far back to search (example output shown)
ntfy_history(since = "1h")
Images
Images can be sent within notifications by specifying as image
either the filename or a ggplot2
object (which will be saved to a temporary file)
library(ggplot2)
p <- ggplot(mtcars, (aes(mpg, wt))) +
geom_point() +
geom_smooth() +
labs(title = "ggplot images in {ntfy}")
ntfy_send("ggplot2 images in notifications!",
tags = c("tada", "chart"),
image = p)
#> `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
Emoji
Supported tags (emoji) can be sent with the tags
argument (one or more). These can be searched or shown with show_emoji()
which will look for a given name in the compatible values, or search for it in the compatible metadata.
The compatible data is stored as emoji
data("emoji")
head(emoji)
#> # A tibble: 6 × 6
#> emoji aliases tags category description unicode_version
#> <chr> <chr> <list> <chr> <chr> <chr>
#> 1 👎 -1 <chr [2]> People & Body thumbs down 6.0
#> 2 👍 +1 <chr [2]> People & Body thumbs up 6.0
#> 3 💯 100 <chr [2]> Smileys & Emotion hundred poi… 6.0
#> 4 🔢 1234 <chr [1]> Symbols input numbe… 6.0
#> 5 🥇 1st_place_medal <chr [1]> Activities 1st place m… 9.0
#> 6 🥈 2nd_place_medal <chr [1]> Activities 2nd place m… 9.0
with the tags stored as tags
for easy auto-complete
The compatible emoji can be shown with
show_emoji("rofl")
#>
#> 🤣 rofl
#>
If the name is not found in aliases
(the compatible names) it will be searched in tags
show_emoji("lol")
#> Unable to find that name directly.
#> Did you perhaps want...
#>
#> 🤣 rofl
#>
show_emoji("pet")
#> Unable to find that name directly.
#> Did you perhaps want...
#>
#> 🐱 cat
#> 🐶 dog
#> 🐹 hamster
#>
You can force this behaviour with
show_emoji("dog", search = TRUE)
#>
#> 🐶 dog
#>
#> Did you perhaps want...
#>
#> 🐩 poodle
#>
Similar Services
- {Rpushbullet} offers similar functionality, but requires sign-up / an API key
- {beepr} can play a sound when a process completes
- IFTTT has an API and can be configured to send messages with e.g. nifffty
- This blog post details many ways to send notifications, via email, text, Slack, and MS Teams
Contributing
If this service is useful to you, consider donating to the developer via GitHub sponsors. If this package is useful to you, I also accept donations via GitHub sponsors.