📜 ⬆️ ⬇️

A couple of ways to send notifications to your smartphone from your server

In this tutorial, I will consider step by step how to send notifications from my server to my (or not) smartphone, what means will be needed for this. These methods are universal and suitable for any programming language, since directly use the Google API, without the use of libraries. You can send to smartphones with Android, iOS and browsers with support for Push API (today it is Chrome, Firefox and their derivatives).

In general, all those who have long wanted to send notifications from their home server to their smartphone, but did not know where to start, is dedicated.

A bit of history. At the beginning (from Android version 2.2), Google used the C2DM system (Android Cloud to Device Messaging) for delivery, since June 2012 they began to suggest using GCM (Google cloud messaging) for this.

At present, the universal Firebase platform is used, which, in addition to the delivery of notifications, has many other possibilities. Firebase also managed to evolve and the first generation protocol is already considered obsolete and it is recommended to use the second generation protocol for message delivery.
')
Technically, notifications are sent from the server not directly to the smartphone, but to a certain intermediate server, where they are stored for up to 4 weeks if necessary (configurable), and, if possible, are sent to the recipient. Those. if the smartphone is offline, the server is waiting. As soon as the opportunity arises, it sends.

1. Registering in Firebase


To register with Firebase, you need a Google account.



Click "Go to console".



Then "Add project".



Enter the name of the project. I recommend in the range of 8-16 characters.
Choose a country. Click "Create a project."

2. Configure Firebase




Scroll to the block "Notifications", click "Start."

You will be prompted to select the application for which your notifications will be sent.



Steps for the Andriod application:



Step 1 - Enter the name of the project on Andriod.
Click "Register an application."



Step 2 - Click "Download google-services.com".
Add the downloaded configuration file to the project, next to the build.gradle file (the one that is personal for the application).
Click "Continue."



Step 3 - Add dependencies to the project.
in the /build.gradle line
classpath 'com.google.gms: google-services: 3.1.0'
in the file /<app-module>/build.gradle line
apply plugin: 'com.google.gms.google-services'
That's all, click "Finish."

After setting up the application, you can immediately test whether the connection works by sending a test message (no, it’s not, we don’t have a client ID where to send it).

3. Setting up the Android application to receive notifications.


Important note: some shells, such as MIUI, may block notifications if the application is not running or not hanging in the background. This is done ostensibly to save battery power.

Roughly speaking, you can send two types of notifications:
- notification on request,
- notification with payload.
They have different ways to interact with the application.

Notification on request will display a notification in the notification area, but only if the application is minimized. When the user clicks it, it will open the application pre-selected (when sending), and transfer the extra parameters to the bundle.

A notification with a payload requires that the application has a pair of services to which control is transferred, but for a duration not longer than 10 seconds.

Below is an example of a service that is responsible for generating a client ID.
package ru.pyur.loga; import android.util.Log; import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceIdService; public class TestFirebaseInstanceIdService extends FirebaseInstanceIdService { public static final String TAG = "TestFbseInstIdSvc"; @Override public void onTokenRefresh() { String refreshedToken = FirebaseInstanceId.getInstance().getToken(); Log.d(TAG, "Refreshed token: " + refreshedToken); //~sendRegistrationToServer(refreshedToken); } } 


And an example of the service code that receives messages. The application must be running, or hang in the background, otherwise the reception of messages is not guaranteed. Some shells, such as MIUI, in order to save, cut everything, including the privileges of background services.

 package ru.pyur.loga; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import static ru.pyur.loga.AcMain.context; public class TestFirebaseMessagingService extends FirebaseMessagingService { public static final String TAG = "TestFbseMsgngSvc"; @Override public void onMessageReceived(RemoteMessage remoteMessage) { Log.d(TAG, "From: " + remoteMessage.getFrom()); if (remoteMessage.getData().size() > 0) { Log.d(TAG, "Message data payload: " + remoteMessage.getData()); String val1 = remoteMessage.getData().get("val1"); String val2 = remoteMessage.getData().get("val2"); String val3 = remoteMessage.getData().get("val3"); int color = (1<<16)|(1<<8)|(0); ShowNotification(val1, val2, color); } if (remoteMessage.getNotification() != null) { Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); } } @Override public void onDeletedMessages() { // In some situations, FCM may not deliver a message. This occurs when there are too many messages (>100) pending for your app on a particular device // at the time it connects or if the device hasn't connected to FCM in more than one month. In these cases, you may receive a callback // to FirebaseMessagingService.onDeletedMessages() When the app instance receives this callback, it should perform a full sync with your app server. // If you haven't sent a message to the app on that device within the last 4 weeks, FCM won't call onDeletedMessages(). } void ShowNotification(String title, String text, int color) { NotificationCompat.Builder mNotify = new NotificationCompat.Builder(context, ""); mNotify.setLights(color, 100, 200); mNotify.setSmallIcon(R.drawable.service_icon); mNotify.setContentTitle(title); mNotify.setContentText(text); mNotify.setDefaults(Notification.DEFAULT_SOUND); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); int mId = 1001; try { mNotificationManager.notify(mId, mNotify.build()); } catch (Exception e) { e.printStackTrace(); } } } 


do not forget to register the service in the manifest.

 <service android:name=".TestFirebaseMessagingService"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter> </service> <service android:name=".TestFirebaseInstanceIdService"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/> </intent-filter> </service> 


Client ID is generated on the device, but you choose the method of delivering this ID to your server.

Now you can test by sending a test message from the console.





4. Send notification from your server


There are several ways to communicate with the Firebase server. We will look at two ways to exchange over HTTP.

First Generation Protocol - Legacy HTTP




You need the key. We press on the nut, select the "Project Settings".



Cloud Messaging tab.
Copy the "Outdated Server Key".

 <?php // ------------------------ test fcm send. legacy ------------------------ // $socket = @fsockopen('ssl://fcm.googleapis.com', 443, $errno, $errstr, 10); if (!$socket) die('error: remote host is unreachable.'); // ----    ---- // $payload = '{ "to" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "notification" : { "title" : "  ", "body" : "(Legacy API) !", "sound": "default" } }'; //  // ----    ---- // $payload = '{ "to" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "data":{ "val1" : "  ", "val2" : "(Legacy API) !", "val3" : "-  " } }'; $send = ''; $send .= 'POST /fcm/send HTTP/1.1'."\r\n"; $send .= 'Host: fcm.googleapis.com'."\r\n"; $send .= 'Connection: close'."\r\n"; $send .= 'Content-Type: application/json'."\r\n"; $send .= 'Authorization: key=AIzaSy***************************IPSnjk'."\r\n"; $send .= 'Content-Length: '.strlen($payload)."\r\n"; $send .= "\r\n"; $send .=$payload; $result = fwrite($socket, $send); $receive = ''; while (!feof($socket)) $receive .= fread($socket, 8192); fclose($socket); echo '<pre>'.$receive.'</pre>'; ?> 

Here in the “to” field you need to substitute the client ID. In the http header, “Authorization: key =”, substitute “Outdated Server Key”.

The second generation protocol is (Modern) HTTP v1.


(source: developers.google.com/identity/protocols/OAuth2ServiceAccount )
Do not ask why the second version of the protocol is called V1, apparently the first was considered a beta and wore a zero number.
I did not go into details, but I understand this protocol is more universal and has more features than just sending notifications.

 <?php // ------------------------ test fcm send. modern ------------------------ // // --  1.  JWT -- // $JWT_header = base64_encode('{"alg":"RS256","typ":"JWT"}'); $issue_time = time(); $JWT_claim_set = base64_encode( '{"iss":"firebase-adminsdk-mvxyi@<your-project>.iam.gserviceaccount.com",'. '"scope":"https://www.googleapis.com/auth/firebase.messaging",'. '"aud":"https://www.googleapis.com/oauth2/v4/token",'. '"exp":'.($issue_time + 3600).','. '"iat":'.$issue_time.'}'); // .  $private_key = ' -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwR1biSUCv4J4W **************************************************************** **************************************************************** ... **************************************************************** teTJImCT6sg7go7toh2ODfaPmeI0nA/LwSjzWs0b8gdIYPT5fAsvfQiND0vu/M3V 7C/z/SmIKeIcfOYrcbWQwTs= -----END PRIVATE KEY----- '; $data = $JWT_header.'.'.$JWT_claim_set; $binary_signature = ''; openssl_sign($data, $binary_signature, $private_key, 'SHA256'); $JWT_signature = base64_encode($binary_signature); $JWT = $JWT_header.'.'.$JWT_claim_set.'.'.$JWT_signature; // --  2.     -- // $socket = @fsockopen('ssl://www.googleapis.com', 443, $errno, $errstr, 10); if (!$socket) die('error: remote host is unreachable.'); $payload = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion='.rawurlencode($JWT); $send = ''; $send .= 'POST /oauth2/v4/token HTTP/1.1'."\r\n"; $send .= 'Host: www.googleapis.com'."\r\n"; $send .= 'Connection: close'."\r\n"; $send .= 'Content-Type: application/x-www-form-urlencoded'."\r\n"; $send .= 'Content-Length: '.strlen($payload)."\r\n"; $send .= "\r\n"; $send .= $payload; $result = fwrite($socket, $send); $receive = ''; while (!feof($socket)) $receive .= fread($socket, 8192); fclose($socket); echo '<pre>'.$receive.'</pre>'; // -- parse answer JSON (lame) -- // $line = explode("\r\n", $receive); if ($line[0] != 'HTTP/1.1 200 OK') die($line[0]); $pos = FALSE; if (($pos = strpos($receive, "\r\n\r\n", 0)) !== FALSE ) { if (($pos = strpos($receive, "{", $pos+4)) !== FALSE ) { if (($pose = strpos($receive, "}", $pos+1)) !== FALSE ) { $post = substr($receive, $pos, ($pose - $pos+1) ); $aw = json_decode($post, TRUE); $access_token = $aw['access_token']; } else die('} not found.'); } else die('{ not found.'); } else die('\r\n\r\n not found.'); // --  3.    Firebase  -- // $socket = @fsockopen('ssl://fcm.googleapis.com', 443, $errno, $errstr, 10); if (!$socket) die('error: remote host is unreachable.'); $payload = '{ "message":{ "token" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "notification" : { "title" : " ", "body" : "(Modern API)     Firebase!" } } }'; //  $payload = '{ "message": { "token" : "cGAFgPJGf-s:APA91bF**...**aEVM17c9peqZ", "data":{ "val1" : " ", "val2" : "(Modern API)     Firebase!", "val3" : " " } } }'; $send = ''; $send .= 'POST /v1/projects/pyur-test-id/messages:send HTTP/1.1'."\r\n"; $send .= 'Host: fcm.googleapis.com'."\r\n"; $send .= 'Connection: close'."\r\n"; $send .= 'Content-Type: application/json'."\r\n"; $send .= 'Authorization: Bearer '.$access_token."\r\n"; $send .= 'Content-Length: '.strlen($payload)."\r\n"; $send .= "\r\n"; $send .=$payload; $result = fwrite($socket, $send); $receive = ''; while (!feof($socket)) $receive .= fread($socket, 8192); fclose($socket); echo '<pre>'.$receive.'</pre>'; ?> 




at console.firebase.google.com/project/poject-id/settings/serviceaccounts/adminsdk, copy the “Firebase Service Account” and substitute it into the “$ JWT_claim_set” variable in the “iss” field.

Click "Create a private key"



Create a key, save, do not show it to anyone. The downloaded file will contain the "Private key", it is substituted into the variable "$ private_key".

Hint: the token obtained in steps 1 and 2 can and should be cached in a local temporary storage, such as a file, or a database. And only after the time has expired (the default is one hour), request the next token from the authorization server.



Important! Before using the Modern Http API, you must explicitly allow its use here: console.developers.google.com/apis/library/fcm.googleapis.com/?project=your-project

Bonus, additional parameters for notifications:


sound - either "default" or the name of the resource in the application. Must be located in "/ res / raw /". MP3 format, AAC or something else suitable.
icon - changes the notification icon. Must be stored in "drawable" applications. If absent, FCM will use the application icon (specified as “launcher icon” in the application manifest).
tag - Should be used to group similar notifications. New notifications will be displayed on top of existing ones with the same tag.
color - the color of the icon, is set as "#rrggbb" (it did not work for me in MIUI)
click_action - activated activation, when the user clicks on the notification.

Conclusion


In the future, the API will probably change, be declared depricated, etc. Therefore, today I think it’s worth doing right away on the HTTP v1 protocol

It will be interesting for me to read in the comments the original ways of using notifications, in addition to new messages from the contact. For example, I have configured monitoring of Arduina fans, and if they stop, a notification is sent.

Yes, I know that there is Zabbix, etc., but the topic of the article is home servers, and other smart houses. I consider corporate-class systems brute-force in amateur handicrafts.

Source: https://habr.com/ru/post/345942/


All Articles