Advanced Android notifications

Hi all,

I've finally found time to implement the service I had been dreaming about for a long time.
Please welcome - FireNotify - Android application with free unlimited advanced notifications

It has API based on the free Google FCM service and allows you to send notifications to your Android phone which could be:

  • ordinary (text, optional subject)
  • silent
  • grouped (several messages from the same group could be collapsed)
  • persistent (cannot be dismissed by user)
  • replaceable (new notification replaces previous having the same id)
  • having custom images
  • actionable (notification with action button(s) generating Node-Red message when pressed)
    and so on

To send notifications from Node-Red the node-red-contrib-fcm-push-node node could be used.
Currently, it requires to specify FCM server key in FCM_SERVER_KEY environment variable (e.g. using settings.js instruction like process.env.FCM_SERVER_KEY = "AAAAaO..."; )

Use the next key for FireNotify service:

FCM_SERVER_KEY=AAAAaOmKSfc:APA91bGabQ9tUXtf5iUOycizl7Wpw0E875EiyxC8E9C2MyfMuG2anvfErYjrwEr4xFsoIVoIalqG4IupliY9436j4HNaL8jgDPEag2S0nSuI3TeU9TTbaK2bj8CFUVBDS6KCZib6Zo0X

And below is JSON content of the message payload that could be used to send a simple message to your phone:

{
	"data": 
	{
		"text": "Hello"
	},
	"to" : "TOKEN_FROM_ANDROID_APPLICATION",
	"priority" : "high"
}

("priority" : "high" is a kind of hack for FCM service to deliver notification message even to the sleeping phone)

All supported parameters are present at the GitHub page of the project

To send an actionable notification you will need:

  1. Install node-red-contrib-fcm-receive-node node to receive FCM messages, add it, register connection for the same FCM server key and check token printed to the Node-Red console

  2. Make the next changes to the JSON of the message payload:

{
	"data": 
	{
		"text": "Hello",
		"to": "TOKEN_FROM_NODE_RED",
		"actions": [
			{"title": "Press me", "data": {"action": "pressed"}}
		]
	},
	"to" : "TOKEN_FROM_ANDROID_APPLICATION",
	"priority" : "high"
}

And notification with single "Press me" action will appear on the screen. When action is pressed - "FCM receive" node will send message with content of "data" property of the action (payload.data.action = "pressed")

Enjoy!

P.S. I know that there could be some issues (for instance token in the Android application appears only on the second start) so your feedback is highly appreciated!

First example to import:

[{"id":"a9ca6f39.541e3","type":"inject","z":"9665c43f.7e5e38","name":"","topic":"","payload":"{\"data\":{\"text\":\"Hello\"},\"to\":\"TOKEN_FROM_ANDROID_APPLICATION\",\"priority\":\"high\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":380,"y":40,"wires":[["1afd39b2.5788e6"]]},{"id":"1afd39b2.5788e6","type":"fcm-push","z":"9665c43f.7e5e38","name":"","x":640,"y":140,"wires":[]}]

Second example to import:

[{"id":"6d296b66.521c74","type":"inject","z":"9665c43f.7e5e38","name":"","topic":"","payload":"{\"data\":{\"text\":\"Hello\",\"to\":\"TOKEN_FROM_NODE_RED\",\"actions\":[{\"title\":\"Press me\",\"data\":{\"action\":\"pressed\"}}]},\"to\":\"TOKEN_FROM_ANDROID_APPLICATION\",\"priority\":\"high\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":380,"y":90,"wires":[["1afd39b2.5788e6"]]},{"id":"1afd39b2.5788e6","type":"fcm-push","z":"9665c43f.7e5e38","name":"","x":640,"y":140,"wires":[]}]
1 Like

Long term, are you planning to monetize this app/service?

No, all sources are open, service is provided by Google and they claim that it will be free

1 Like

This looks like a really useful contribution @eschava, thanks for sharing.

Where is this added? I've loaded your example push flow, added my Android app token, but not working, I assume because I haven't added the FCM_SERVER_KEY ??

snip

It's OS environment variable and it could be set using export command for Linux or set command for Windows before starting Node-Red process

Aah right, hopefully future development will avoid having to do this.
I'll come back at a later time.

Easier to set it in settings.js as you can then use Node.js's feature to set environment variables - that is cross-platform.

1 Like

This is awesome, thanks! Haven't yet tried it so I am not sure if already implemented but does firenotify also provide a history overview of notifications received?

Not yet. I have this in ideas for further development but now you can use 3rd party applications storing history of notifications for any app
You can choose any from https://play.google.com/store/search?q=notification%20history and share your expirience

1 Like

Got t to work! However when trying out the receive node, no token was printed to node red's console. I got this instead :):

(node:23977) UnhandledPromiseRejectionWarning: StatusCodeError: 400 - "{\n  \"error\": {\n    \"code\": 400,\n    \"message\": \"Request contains an invalid argument.\",\n    \"status\": \"INVALID_ARGUMENT\"\n  }\n}\n"
    at new StatusCodeError (/home/pi/.node-red/node_modules/request-promise-core/lib/errors.js:32:15)
    at Request.plumbing.callback (/home/pi/.node-red/node_modules/request-promise-core/lib/plumbing.js:104:33)
    at Request.RP$callback [as _callback] (/home/pi/.node-red/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at Request.self.callback (/home/pi/.node-red/node_modules/request/request.js:185:22)
    at emitTwo (events.js:126:13)
    at Request.emit (events.js:214:7)
    at Request.<anonymous> (/home/pi/.node-red/node_modules/request/request.js:1154:10)
    at emitOne (events.js:116:13)
    at Request.emit (events.js:211:7)
    at IncomingMessage.<anonymous> (/home/pi/.node-red/node_modules/request/request.js:1076:12)
    at Object.onceWrapper (events.js:313:30)
    at emitNone (events.js:111:20)
    at IncomingMessage.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:139:11)
    at process._tickCallback (internal/process/next_tick.js:181:9)
(node:23977) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:23977) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Like this? - see post below

process.env.FCM_SERVER_KEY=AAAAaOmKSfc:APA91bGabQ9tUXtf5iUOycizl7Wpw0E875EiyxC8E9C2MyfMuG2anvfErYjrwEr4xFsoIVoIalqG4IupliY9436j4HNaL8jgDPEag2S0nSuI3TeU9TTbaK2bj8CFUVBDS6KCZib6Zo0X;

Added to the top of settings.js

1 Like

like this :slight_smile:

process.env.FCM_SERVER_KEY="AAAAaOmKSfc:APA91bGabQ9tUXtf5iUOycizl7Wpw0E875EiyxC8E9C2MyfMuG2anvfErYjrwEr4xFsoIVoIalqG4IupliY9436j4HNaL8jgDPEag2S0nSuI3TeU9TTbaK2bj8CFUVBDS6KCZib6Zo0X";

Note the double quotes.

2 Likes

Is there a way to send a url, which opens when clicked on the notification?

There are different explanation on the internet about this error and some of them are about incorrect FCM server key, could you please recheck whether it is correct?

Is there a way to send a url, which opens when clicked on the notification?

Good idea. And I would rather add the url to open as an option for an action, not for clicking on the notification itself. What do you think?

2 Likes

Yes - we have enough trouble with dodgy links as it is... so allowing a confirmation/option first is sensible

1 Like

Yes, I had the wrong key. I think :). Now it throws this error:


(node:24386) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '/home/pi/.node-red/fcm-receive/bc4b8b5d.ba9948/fcm.cred'
    at Object.fs.openSync (fs.js:646:18)
    at Object.fs.writeFileSync (fs.js:1299:33)
    at FcmConnection (/home/pi/.node-red/node_modules/node-red-contrib-fcm-receive-node/fcm-connection.js:28:20)
    at <anonymous>
(node:24386) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

Seems it tries to write to a file which is not created yet?

Seems it tries to write to a file which is not created yet?

Please check whether /home/pi/.node-red/fcm-receive/bc4b8b5d.ba9948/ folder exists
I'm not an expert in node.js development and wrote this plugin using stackoverflow-driven methodology :slight_smile:

That directory should be created by previous instruction
fs.mkdirSync(dir, { recursive: true });

Folder doesn't exist, so something is going off there. :(. I'll try creating it manually to see what happens then.

Edit: yep, that worked. Now I get a token.

Edit 2: And the message is received back in Node Red, pretty nice!! Although it is not very clear on my phone that I 'pressed' a button in the notification, which caused me pressing it tenfold :slight_smile:

Edit 3: Every message received back also throws an error in the console:

{ Error: read ECONNRESET
    at TLSWrap.onread (net.js:622:25) errno: 'ECONNRESET', code: 'ECONNRESET', syscall: 'read' }
1 Like