2

I'm trying to update a previous notification (which displays the screen's brightness after being changed using a progress bar) with gdbus, as notify-send does not support updating.

Previously I use a workaround by killing the previous notification, but it makes the notification disappear and then reappear, instead of smoothly updating the progress bar:

#!/bin/bash
for i in {0..100..10}
    do
        kill $(pgrep ^xfce4-notifyd$)
        notify-send "Brightness" -h int:value:$(xbacklight -get) -h string:synchronous:volume -i weather-clear -t 1
    done

I tried dbus-send, but no notification showed up even if I remove the hints part. After some googling, I found out gdbus and got it working, but only without hints.

Basically, this is where I've got so far with gdbus:

#!/bin/bash
gdbus call --session --dest org.freedesktop.Notifications \
--object-path /org/freedesktop/Notifications \
--method org.freedesktop.Notifications.Notify \
brightness \
42 \
notification-display-brightness-full \
"Message" "Body" [] \
"{'value':i, 'name':'value', 'value':'$(xbacklight -get)'}" "{'type':'string', 'name':'synchronous', 'value':'volume'}" \
1

However I'm stuck at this error:

Error parsing parameter 7 of type 'a{sv}': expected value:
  {'value':i, 'name':'value', 'value':'0.000000'}
           ^

Can anyone help me with the syntax for notification hints sent by gdbus?

I read somewhere that I can use some custom patched version of notify-send, however I don't like the idea of introducing unofficial binaries into my stable system.

Btw, I'm using xfce4-notifyd in xubuntu.

UPDATE:

I tried to be less pig-headed and I tried python's dbus module for the job. And again, I'm stuck at hints. It only worked if I remove the hints. Here's the new code:

#!/usr/bin/env python3
"""Creates a Notification pop-up bubble"""
import dbus
item              = "org.freedesktop.Notifications"
path              = "/org/freedesktop/Notifications"
interface         = "org.freedesktop.Notifications"
app_name          = "brightness"
id_num_to_replace = 0
icon              = "weather-clear"
title             = "Message"
text              = "Body"
actions_list      = ''
hint              = '"{'type':i, 'name':'value', 'value':'$(xbacklight -get)'}" "{'type':'string', 'name':'synchronous', 'value':'volume'}"'
time              = 5000   # Use seconds x 1000

bus = dbus.SessionBus()
notif = bus.get_object(item, path)
notify = dbus.Interface(notif, interface)
notify.Notify(app_name, id_num_to_replace, icon, title, text, actions_list, hint, time)

And the error:

File "/home/pygeek03/bin/brightness.py", line 13
 hint              = '"{'type':i, 'name':'value', 'value':'$(xbacklight -get)'}" "{'type':'string', 'name':'synchronous', 'value':'volume'}"'
                           ^
SyntaxError: invalid syntax
PyGeek03
  • 168
  • Did you mean $i in your original bash script? – muru Jun 20 '16 at 06:20
  • Well i stands for int32 type, according to dbus's specification. As to the initial enthusiasm when I first saw your comment, I mistook it with the notify-send script. – PyGeek03 Jun 20 '16 at 06:25
  • I used the hints to create a progress bar, and it worked with notify send ( -h int:value:$(xbacklight -get) -h string:synchronous:volume ) so I was figuring out how to translate it to dbus's syntax for message... – PyGeek03 Jun 20 '16 at 06:28
  • If that is JSON or something similar, then it would be 'value': 'i', for the string i, or 'value': $i, so that i's value is passed on (if an integer). – muru Jun 20 '16 at 06:30
  • I guess it has something to do with the way xfce4-notifyd was implemented... If only I could understand the source code of libnotify in order to figure out how they implemented --hint option in notify-send... – PyGeek03 Jun 20 '16 at 08:37
  • @PyGeek03 With gdbus a{sv} should be represented as "{'String': <'variant_value'>, 'String2': <'variant_value'>}". "Did you try with that? See http://askubuntu.com/questions/359587 – Khurshid Alam Jun 28 '16 at 16:38
  • @KhurshidAlam Well I used that exact syntax, but the problem seems to be with how notifyd interpret the a{sv} as hint for the notification. It's reflected in the first error in my question. – PyGeek03 Jun 28 '16 at 16:56
  • @KhurshidAlam I even consulted gdbus documentation on how to represent type in a dbus message (in this case, an int), and in that documentation, it stated that i stands for type int32. When I tried i (as you can see in the batch script), the error said that it's expecting another value, but refused to let me know what that expected value is (as you can see in the error). – PyGeek03 Jun 28 '16 at 16:59

2 Answers2

-1

https://github.com/bkw777/notify-send.sh does it
Pure bash other than using gdbus call to do the actual dbus transaction.
It's main reason for existing is actually to replace existing notification, so the readme shows exactly that example.

Aside from reading the code, you can enable debugging and read the final gdbus command out of the debug log.

The gdbus commandline 2nd to last arg contains $h, and backtrack from there $h is built from ${HINTS[*]}, which is built by one or more calls to process_hint and make_hint (returns it's output via global $_r to avoid subshells). There is always at least one to set the urgency level even if you don't supply any --hint options.

To replace a notification, you need to know it's ID, and to know it's ID you have to collect it when it was created the first time. (or you could try to just generte your own ID which you hope will be unique and never overwrite some other notification, but the clean way is to let the notification daemon generate the notification the first time, and then use that to update it after that.)

Send a notification and collect it's ID with debugging enabled so you can see the generated gdbus command

$ DEBUG_NOTIFY_SEND=true notify-send.sh --print-id --expire-time=0 --close-action="mainline-gtk --install 6.3.4" "New Kernel" "Install 6.3.4"
/usr/local/bin/notify-send.sh debug logging to /run/user/1000/.notify-send.sh.1044020.e
68
$

notification ID was 68

gdbus command was:

$ grep "gdbus call" /run/user/1000/.notify-send.sh.1044020.e
++ gdbus call --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications --method org.freedesktop.Notifications.Notify -- notify-send.sh 0 '' 'New Kernel' 'Install 6.3.4' '[]' '{"urgency":<byte 1>}' 0
$

now replace that notification in-place, using --replace=68

$ DEBUG_NOTIFY_SEND=true notify-send.sh --replace=68 --expire-time=0 --close-action="mainline-gtk --install 6.3.5" "New Kernel" "Install 6.3.5"
/usr/local/bin/notify-send.sh debug logging to /run/user/1000/.notify-send.sh.1044042.e
$ grep "gdbus call" /run/user/1000/.notify-send.sh.1044042.e
++ gdbus call --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications --method org.freedesktop.Notifications.Notify -- notify-send.sh 68 '' 'New Kernel' 'Install 6.3.5' '[]' '{"urgency":<byte 1>}' 0
$

So hints are of the form: {'name':<type value>}

with some non-obvious rules and allowed deviations:

  • the entire string has to be enclosed in quotes or escaped with backslashes since this is all on a bash command line, and the exact syntax is "whatever you want that results in a valid bash commandline..." since there's more than one way to quote things. Exept one really non-obvious, name must be individually quoted, single or double, even if it has no spaces, name's quotes must themselves be either escaped or quoted so that bash doesn't eat them so they become literals and part of the string.
    The script is generating: '{"urgency":<byte 1>}'
    also valid: "{'urgency':<byte 1>}"
    also valid: "{\"urgency\":<byte 1>}"
    NOT valid: \{'urgency':\<byte\ 1\>\}
    IS valid: \{\'urgency\':\<byte\ 1\>\}

  • type is optional, at least for some types, at least for byte and int32
    valid: "{'urgency':<byte 1>}"
    also valid: "{'urgency':<1>}"

  • https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html#hints has a table that shows all the types as BYTE INT32 STRING etc, but they must actually be sent in lower-case.
    valid: "{'urgency':<byte 1>}"
    not valid: "{'urgency':<BYTE 1>}"

  • Not shown yet but multiple hint items are comma seperated within the braces
    "{\"urgency\":<byte 1>,\"category\":<string \"info\">}"

Here's another with more stuff filled in, added an icon name and another hint for category, and action buttons so all fields have stuff in them, and actions and hints are both multi-item lists:

$ notify-send.sh --expire-time=0 --force-expire --category=info --icon=mainline --action="Mainline App:mainline-gtk" --action="Install 6.3.4:mainline-gtk --install 6.3.4" "Mainlin Kernels" "New kernel available"
/usr/local/bin/notify-send.sh debug logging to /run/user/1000/.notify-send.sh.1148819.e
$ grep "gdbus call" /run/user/1000/.notify-send.sh.1148819.e
++ gdbus call --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications --method org.freedesktop.Notifications.Notify -- notify-send.sh 0 mainline 'Mainlin Kernels' 'New kernel available' '["0","Mainline App","1","Install 6.3.4"]' '{"urgency":<byte 1>,"category":<string "info">}' 0
$

You may have noticed that these examples include an action that doesn't show up in the gdbus command.

Actions get handled by setting up a gdbus monitor command, and grepping the notification ID from the monitor process's output.
The gdbus monitor will output multiple messages, and you ignore any that don't have your ID. When you get a record with your ID in it, parse the action out of that line, and depending upon what the action was, maybe run the command you were previously given.

The action commandline is never written to dbus. What happens is notify-send.sh runs notify-action.sh and notify-action.sh waits for essentially "notification ID 68 closed" and when it sees that, it runs the command that notify-send.sh gave it previously.

If you use an action button instead of close-action, you give that button some unique key (you make it up, can be anything, simplest is just a number, one for each button, can start from the same 0 or 1 every time since it only needs to be unique per notification ID, so 3 buttons can be keys 1 2 3), and that goes onto dbus, and later the watcher process looks for essentially "notification 68 got action 2" and notify-action.sh has previously been given a list of button IDs and associated commands, so when it sees "got ID 68 action 2" it runs the command for action 2.

  • If this is your script, please state so explicitly – muru Jun 27 '23 at 09:17
  • the question was about some syntax, and the answer was about that syntax, anything else is irrelevant – Brian White Jun 27 '23 at 09:32
  • then ... just remove the irrelevant stuff about your script and focus on the syntax? – muru Jun 27 '23 at 09:36
  • An actual working example implementation is not irellevant. Please find a new hobby. – Brian White Jun 27 '23 at 12:34
  • Then ... Just clarify that the script you're advertising is yours? – muru Jun 27 '23 at 12:43
  • Who's advertising? Is anyone selling anything? Are any names hidden? Meanwhile, ff you're the one who downvoted, you should show how it is incorrect in any way. You seem to be trying to imply something about integrity, meanwhile, you downvote something that is not in error, apparently for personal reasons. You are in a poor position from which to talk about anyone else about being forthright. – Brian White Jun 27 '23 at 12:47
  • If the names are not hidden, then what's the problem in making it explicit? – muru Jun 27 '23 at 13:40
  • If the information was incorrect or not applicable, what's the problem explaining how so exactly? – Brian White Jun 27 '23 at 14:21
  • Which information? Authorship? It is relevant, as your username may change, either here or on GitHub, so it is important to be explicit – muru Jun 27 '23 at 14:38
  • The information about the gdbus, and specifically, from bash, which are the two topics in the posted question. Who cares if anyone's usernames change? What does that have to do with gdbus? What problem do you think you are adressing with this silly crusade? Do the gdbus commands above somehow harm the user? If so, how? Quote any of the the gdbus commands, and explain in what way it either fails to address the question, or harms a potential reader. Failing that, get stuffed. – Brian White Jun 27 '23 at 15:06
  • Whoa, mate, calm down, I'm just asking you to state whether you wrote the script you linked to in the answer, around which a lot of this answer revolves (it keeps saying the script does this, the script does that, etc.), even though you say that the question is about syntax and your answer is supposed to be about syntax. It'd take what, 5 words, to clarify that? – muru Jun 27 '23 at 16:14
  • And I keep asking what's it to you? get stuffed "mate". You're concern is kind of interesting since you bring up the menacing spectre of "username might change" my name is my actual name, both here and on that github and at the top of that script, whille yours is an anonymous handle. Seriously you are so full of S with this pogrom over nothing it's funny. I'm not hiding anything, I just decline to cooperate with some randos demand to do something irrelevant. I'll argu about it, but not dignify it. You must be fun at parties. – Brian White Jun 27 '23 at 22:39
  • Really, bkw777 is your actual name? I have told you what's it to me: it's important that people clarify then they're linking to their own scripts, that's all. – muru Jun 28 '23 at 00:53
  • When you click on my name, what do you see? When I click on your name, what do I see? You can not only see my real name and email, you can probably find my cell phone and home address in seconds. "Your argument is invalid" – Brian White Jun 28 '23 at 18:41
  • And since I never made any attempt to even slightly obscure who wrote what, again, "your argument is invalid"

    And since, the code and the generated gdbus syntax speaks for itself, and works or doesn't work regardless where it came from, and can't possibly hide a back door, and isn't trying to sell or take anything, again "your argument is invalid".

    There's just nothing there to justify your attempt to tell me to do something. You could have asked. "Did you write that?" that would have been an entirely different conversation.

    – Brian White Jun 28 '23 at 18:52
  • You also still haven't explained the downvote. In what way does the information fail to adress or apply to the question? Did you try it and it doesn't actually work? Do you know something about how dbus works that I got wrong? Why aren't you sharing that wisdom with posterity? I would like my own stuff to be as correct as possible and I would appreciate it. – Brian White Jun 28 '23 at 18:55
  • "You could have asked. "Did you write that?" that would have been an entirely different conversation." Is that really all that different from "If this is your script, please state so explicitly"? – muru Jun 29 '23 at 00:35
  • "Is that really all that different from "If this is your script, please state so explicitly"?" yes – Brian White Aug 03 '23 at 00:04
  • Well, then: Did you write that? If so, please edit your answer to explicitly mention that. – muru Aug 03 '23 at 00:09
-1

It's just invalid python syntax. What you do with the strings for hint does not make any sense. Try seeing matching quotes for example.

hint should be: hint = {'type':'i', 'name':'value', 'value':int(os.system('xbacklight -get'))}