Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notifications are not working in version 13.x+ #1578

Closed
1 task done
Darrilla opened this issue Jan 28, 2025 · 21 comments
Closed
1 task done

Notifications are not working in version 13.x+ #1578

Darrilla opened this issue Jan 28, 2025 · 21 comments
Labels
discussion Let's talk about it

Comments

@Darrilla
Copy link

Darrilla commented Jan 28, 2025

Is there an existing issue for this?

  • I have searched the existing issues

Describe The Bug

I have followed all of the steps for troubleshooting:

  • Remove all extra client devices in the ring control center
  • Reauth and reissue refresh token
  • Verify connection for each device to port 5228 (firebase):
    • netstat -ant|grep 5228
      tcp4 0 0 10.0.0.86.60023 74.125.137.188.5228 ESTABLISHED
      tcp4 0 0 10.0.0.86.55387 142.251.2.188.5228 ESTABLISHED

When I push the doorbell button or create motion, I see it in the ring app. My phone gets the notification, however I never get any kind of event in my code.

in my code:

if (this.camera) {
        console.log('Found doorbell: ', this.camera?.name);
        if (this.camera.isOffline) {
          this.setOffline(ErrorCode.CONNECTION_ERROR_DEVICE_OFFLINE);
        } else {
          this.setOnline();
        }
        this.camera.onNewNotification.subscribe((notification) => {
          const action = notification.android_config.category;
          const event =
            action === PushNotificationAction.Motion
              ? "Motion detected"
              : action === PushNotificationAction.Ding
                ? "Doorbell pressed"
                : `other ${action}`;

          console.log(
            `${event} on ${this.camera?.name} camera. Ding id ${
              notification.data.event.ding.id
            }.  Received at ${new Date()}`,
          );
        });
        this.camera.onDoorbellPressed.subscribe(
          ({
            data: {
              event: {
                ding: { created_at },
              },
            },
          }) => {
            console.log(`Doorbell pressed on ${this.camera?.name}`);
          },
        );
        this.camera.onDoorbellPressed.subscribe(this.onDoorbellEvent.bind(this));
        this.camera.onMotionDetected.subscribe(this.onMotionEvent.bind(this));
      } else {
        this.falcon.logger.error('Error starting Ring Doorbell driver - Unable to find Ring Doorbell');
        this.setOffline(ErrorCode.DEVICE_ERROR_NOT_FOUND);
      }

with these handlers:

onDoorbellEvent(value: any) {
  this.setOnline();
  console.log('Doorbell Event:', JSON.stringify(value, null, 2));
}

onMotionEvent(sensingMotion: boolean) {
  this.setOnline();
  console.log('Doorbell Motion:', sensingMotion);
}

The logs do show:
Found doorbell: Back Door
and it seems to make a call to the callback a single time:
Doorbell Motion: false

but then nothing after that...

To Reproduce

Authenticate using email and password, followed by the 2fa code. Save the refresh token, and use it in further communication.
Register for motion and ding events (and even the generic notifications)
Press the doorbell button or make movement - note the ring app and phone receive notifications, but there is nothing sent to the ring-client-api application.

Expected behavior

I expect to receive a notification whenever there is motion or the doorbell is dinged

Relevant log output

Screenshots

No response

Additional context

No response

OS

macOs and Linux

Node.js Version

v20.18.0

NPM Version

v10.8.2

ring-client-api

v13.2.1

Operating System

macOS and Linux

@Darrilla Darrilla added the bug Something isn't working label Jan 28, 2025
@tsightler
Copy link
Collaborator

Do you get any logs if you start the API with debug? Why are there two connections to firebase, are you running two instances with the same token? I would expect there to only be a single connection (the connections are not per-device). Are you subscribing to api.onRefreshTokenUpdated and saving the token updates from the very first time?

@tsightler tsightler added discussion Let's talk about it and removed bug Something isn't working labels Jan 28, 2025
@Darrilla
Copy link
Author

One of those connections was from a Google process, not sure which one. I only have one:
netstat -ant|grep 5228
tcp4 0 0 10.0.0.86.62146 142.251.2.188.5228 ESTABLISHED

lsof -i :5228

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 71778 dburns 83u IPv4 0x8cc32762a028c11e 0t0 TCP 10.0.0.86:62146->dl-in-f188.1e100.net:5228 (ESTABLISHED)
I tried debug: true, but seems to add nothing to the equation:

  ringApi = new RingApi({ refreshToken, debug: true });

Yes, I am subscribed to that onRefreshTokenUpdated. This has worked great for 2-3 years, but I was on the 12.X version, and when notifications stopped, I upgraded to 13.2.1 to hopefully get the notifications working again. So this has been good, and even with 12.x, I am able to get snapshots and video clips, just unable to get the motion or ding events...

@tsightler
Copy link
Collaborator

Firebase killed the legacy API for push notification back in July and Ring moved to a new message format around that same time as required by the new push API, so I'm completely surprised that it stayed working since Firebase de-commissioned most of the old push infrastructure around Sept, based on my understanding. Because of this, the chance of v12 working is very low.

However, v13 ring-client-api has been deployed for a long time now and, while there were some issues with early versions due to instability in upstream push-receiver, and with the move to use native fetch, the more recent releases have proven to be very stable, so I'm not sure what more can be suggested.

I saw 1-2 reports of a similar issue with ring-mqtt on the Home Assistant forums, but these cases were corrected with various account setting changes, not anything from a code perspective.

If you are completely stopping the client, deleting it from Ring Control Center, generating an entirely new, unique token, and restarting the process on this single instance (sharing tokens to different instances is good way to make this break), keeping every refresh token along the way, then there's not much I can help with.

In the end, the overall process is simple, push-receiver registers with FCM and creates the socket connection, which it appears is successful in your case, ring-client-api takes the FCM token and registers it with Ring API. This token must stay persistent after this point as it can't be changed after initial registration, and this is why storing refresh tokens for every refresh is critical. After that, Ring sends messages to FCM, which sends it to the client connected with that token. The messages either arrive or do not, there's not much that can be done if they do not, other than attempting to dig into the API and compare to the client.

However, every single case where a user has complained of this issue and shared their account with me, it has worked fine for me, which indicates to me that the code is not at fault. But, of course, all the source code is there, you are welcome to dig into it and send a PR if you can improve it.

@Darrilla
Copy link
Author

I still am unable to determine why this isn't working. I have noticed that the authorized device, with 12.x, had a name ring_client_api, but now it say "Device name not found". Is this expected?

@tsightler
Copy link
Collaborator

I still am unable to determine why this isn't working. I have noticed that the authorized device, with 12.x, had a name ring_client_api, but now it say "Device name not found". Is this expected?

There are no changes around API handling in v13 regarding devince, so I would expect it to be the same. Ideally, you would set the device name to something uniquely identifying via the controlCenterDisplayName config option, but basically the code just uses "ring_client_api" if this is not set. That code is unchanged in forever.

I also noticed that you are not generating and passing a persistent system ID, I remember there being some issues in the past with notifications when this isn't done, so maybe that's a problem? Ring-mqtt, ring-homebridge, and Ring io.broker projects all generate and store their own persistent systemId.

@spencerfairchild
Copy link

Just to add to this. I am also unable to get any motion events for cameras. I am running this test program:

import { RingApi, PushNotificationAction } from 'ring-client-api'

const ringApi = new RingApi({
  refreshToken: 'TOKEN',
})

const locations = await ringApi.getLocations()
const location = locations[0]

const camera = location.cameras[0]

console.log(`Subscribing to camera ${camera.name}`)
camera.onNewNotification.subscribe((notification) => {
        const action = notification.android_config.category,
          event =
            action === PushNotificationAction.Motion
              ? 'Motion detected'
              : action === PushNotificationAction.Ding
              ? 'Doorbell pressed'
              : `Video started (${action})`

        console.log(
          `${event} on ${camera.name} camera. Ding id ${
            notification.data.event.ding.id
          }.  Received at ${new Date()}`,
        )
      })

camera.onMotionDetected.subscribe((test) => {
	console.log(`Motion detected on ${camera.name} camera. Received at ${new Date()} - ${test}`)
})

setInterval(() => {}, 1000); 

The only notification I see coming back is an initial "On motion detected" as false and then I never see anything else. The ring app gives me a push but nothing comes through from firebase (I verified there is a connection open)

@Darrilla
Copy link
Author

I used npx -p ring-client-api ring-auth-cli, and connected - it also shows up in my Ring control center as "Device name not found"...

@tsightler
Copy link
Collaborator

tsightler commented Jan 29, 2025

@spencerfairchild

The only notification I see coming back is an initial "On motion detected" as false and then I never see anything else. The ring app gives me a push but nothing comes through from firebase (I verified there is a connection open)

I copied your example code to test.js, acquired a token and ran it and it worked perfectly:

$ node test.js
Subscribing to camera Front Doorbell
Motion detected on Front Doorbell camera. Received at Wed Jan 29 2025 15:10:14 GMT-0500 (Eastern Standard Time) - false
Motion detected on Front Doorbell camera. Received at Wed Jan 29 2025 15:10:42 GMT-0500 (Eastern Standard Time) - true
Motion detected on Front Doorbell camera. Ding id 7465432443625829061.  Received at Wed Jan 29 2025 15:10:42 GMT-0500 (Eastern Standard Time)
Doorbell pressed on Front Doorbell camera. Ding id 7465432701323866821.  Received at Wed Jan 29 2025 15:11:42 GMT-0500 (Eastern Standard Time)
Motion detected on Front Doorbell camera. Received at Wed Jan 29 2025 15:11:46 GMT-0500 (Eastern Standard Time) - false

It's important to note that the code as written will only work the very first time it is used with a newly generated refresh token, where the device was completely removed from Ring Control Center, because you are not saving updated refresh tokens, which is critical for future use.

Again, I would also highly recommend generating and saving a persistent systemId and sending that as part of the API connection config as well. Examples on how to do this are available in ring-homebridge and ring-mqtt.

@Darrilla
Copy link
Author

Darrilla commented Jan 29, 2025

It's important to note that the code as written will only work the very first time it is used with a newly generated refresh token, where the device was completely removed from Ring Control Center, because you are not saving updated refresh tokens, which is critical for future use.

Again, I would also highly recommend generating and saving a persistent systemId and sending that as part of the API connection config as well. Examples on how to do this are available in ring-homebridge and ring-mqtt.

Please, can you tell what you did differently here? This has been attempted on more than one ring acount, clearing out the Ring Control Center each time. I also can confirm when I did it it does call the onMotionDetected callback a single time with 'false', but then never again.

Can you provide the test.js program you used?

I'm sure I'm missing something here...Oh, and how do I run the typescript examples in this repo?

@tsightler
Copy link
Collaborator

I used npx -p ring-client-api ring-auth-cli, and connected - it also shows up in my Ring control center as "Device name not found"...

Immediately after authentication it will show as "Device name not found", however, once you use the token to connect to the API, it should change to 'ring-client-api'. I just tested this exact flow again, using the example code posted by @spencerfairchild and it worked exactly as I described. Again, you can make it show whatever name you want using controlCenterDisplayName config option when connecting to the API.

Unfortunately, the fact that all tests work fine for me, I don't really have any other way to help you out. I can't force Ring to send messages to your endpoint and I know of no reason why it would not do so. I suppose if you are willing to give me your Ring account credentials, I can test it myself. I know that's intrusive, but there's not really any other option that I see as I can't fix a non-reproducible problem.

One thing I did notice, your FCM endpoints appear to be in US west coast, is that your location?

@tsightler
Copy link
Collaborator

Can you provide the test.js program you used?

As I already said, it's just the code @spencerfairchild posted above. I just copied and pasted it exactly, saved it as test.js, replaced the token with my freshly generated token, it worked, no issues.

@Darrilla
Copy link
Author

One thing I did notice, your FCM endpoints appear to be in US west coast, is that your location?

Yes, this is the correct location. We are in Utah. I just managed to get mine to work using the test code:

node test.js
Subscribing to camera Back Door
Motion detected on Back Door camera. Received at Wed Jan 29 2025 13:43:59 GMT-0700 (Mountain Standard Time) - false
Doorbell pressed on Back Door camera. Ding id 7465441158413146265. Received at Wed Jan 29 2025 13:44:31 GMT-0700 (Mountain Standard Time)
Doorbell pressed on Back Door camera. Ding id 7465441493420595353. Received at Wed Jan 29 2025 13:45:48 GMT-0700 (Mountain Standard Time)
Motion detected on Back Door camera. Received at Wed Jan 29 2025 13:48:57 GMT-0700 (Mountain Standard Time) - true
Motion detected on Back Door camera. Ding id 7465442296579479705. Received at Wed Jan 29 2025 13:48:57 GMT-0700 (Mountain Standard Time)
Motion detected on Back Door camera. Received at Wed Jan 29 2025 13:50:02 GMT-0700 (Mountain Standard Time) - false

It seems to be hit-or-miss

@tsightler
Copy link
Collaborator

It seems to be hit-or-miss

I'm not really sure what this means, push notifications are the same notifications you receive on your phone/device or the Ring web console, they cannot be any more hit-or-miss than the Ring app itself as it is 100% identical in process. Glad you got it to work.

@Darrilla
Copy link
Author

Darrilla commented Jan 31, 2025

I borrowed some code from the ring-client-api refresh-token and utils, and I have a test program that works every time. This indicates something is wrong in the logic of my main program.

By the way, this only works one time. In order to get it to work a second time, you must go in to the control center and removed the authorized device 'Ring Events Tester'

For reference, I include the test program here in case anyone else runs into something similar:

import { createInterface } from 'readline';
import { RingRestClient } from 'ring-client-api/rest-client';
import { RingApi, PushNotificationAction } from 'ring-client-api';

const requestInput = async (question) => {
  const lineReader = createInterface({
      input: process.stdin,
      output: process.stdout,
  });
  const answer = await new Promise((resolve) => {
      lineReader.question(question, resolve);
  });

  lineReader.close();

  return answer.trim();
};

const email = await requestInput('Email: ');
const password = await requestInput('Password: ');

const ringRestClient = new RingRestClient({
  email,
  password,
});

const getAuthWith2fa = async ()=> {
  const code = await requestInput('2fa Code: ');
  try {
   return await ringRestClient.getAuth(code);
  } catch (_) {
    console.log('Incorrect 2fa code. Please try again.');
    return getAuthWith2fa();
  }
}

const auth = await ringRestClient.getCurrentAuth().catch((e) => {
  if (ringRestClient.promptFor2fa) {
    console.log(ringRestClient.promptFor2fa);
    return getAuthWith2fa();
  }

  console.error(e)
  process.exit(1)

});


const refreshToken = auth.refresh_token;
console.log('RefreshToken:', refreshToken);

const ringApi = new RingApi({
      controlCenterDisplayName: Ring Events Tester',
      refreshToken,
})

const locations = await ringApi.getLocations()
const location = locations[0]

const camera = location.cameras[0]

console.log(`Subscribing to camera ${camera.name}`)
camera.onNewNotification.subscribe((notification) => {
        const action = notification.android_config.category,
          event =
            action === PushNotificationAction.Motion
              ? 'Motion detected'
              : action === PushNotificationAction.Ding
              ? 'Doorbell pressed'
              : `Video started (${action})`

        console.log(
          `${event} on ${camera.name} camera. Ding id ${
            notification.data.event.ding.id
          }.  Received at ${new Date()}`,
        )
      })

camera.onMotionDetected.subscribe((test) => {
	console.log(`Motion detected on ${camera.name} camera. Received at ${new Date()} - ${test}`)
})

setInterval(() => {}, 1000); 

@tsightler
Copy link
Collaborator

By the way, this only works one time. In order to get it to work a second time, you must go in to the control center and removed the authorized device 'Ring Events Tester'

Because you have to monitor for token updates and save them for future re-use, as I've mentioned multiple times and is covered completely in the wiki.

@mahdirahman1
Copy link

mahdirahman1 commented Feb 2, 2025

I have the same issue despite using ringApi.onRefreshTokenUpdated.subscribe which is what is recommended to handle update of refresh tokens

import "dotenv/config";
import { RingApi } from "ring-client-api";
import { readFile, writeFile } from "fs";
import { promisify } from "util";


async function example() {
	const ringApi = new RingApi({
			refreshToken: process.env.RING_REFRESH_TOKEN,
			debug: true,
		}),
		cameras = await ringApi.getCameras(),
		camera = cameras[0];
	
	ringApi.onRefreshTokenUpdated.subscribe(
		async ({ newRefreshToken, oldRefreshToken }) => {
			if (!oldRefreshToken) {
				return;
			}

			const currentConfig = await promisify(readFile)(".env"),
				updatedConfig = currentConfig
					.toString()
					.replace(oldRefreshToken, newRefreshToken);

			await promisify(writeFile)(".env", updatedConfig);
		}
	);


	camera.onNewNotification.subscribe((pushNotif) => {
		console.log("New notification");
		if (pushNotif.subtype === "motion" || pushNotif.subtype === "ding") {
			// start recording
			console.log("here");
		}
	});


	camera.onMotionDetected.subscribe((pushNotif) => {
		console.log("Motion detected");
		console.log(pushNotif);
	});

	camera.onDoorbellPressed.subscribe((pushNotif) => {
		console.log("Doorbell pressed");
	});

	
}

example().catch((e) => {
	console.error(e);
	process.exit(1);
});

@tsightler
Copy link
Collaborator

@mahdirahman1 I ran your code exactly as you posted and it works fine, and the code in this library is leveraged in products used by more than 10,000 users. If you are having an issue, you have to troubleshoot your specific case. I've already written everything I can ever write about it multiple times, and I just don't have patience to type the same information again.

One thing important to note, especially if you have multiple cameras, the code above just randomly monitors the first camera returned in the array, this might not be consistent between runs.

In the end, all source code is here, if you find something wrong with it, we welcome a PR, but I don't know what else to do, the problems are always the same as what has already been covered.

@mahdirahman1
Copy link

@tsightler Thanks for your reply, I was on version 12.x of the api, upgraded to v13.x and all good!

@tsightler
Copy link
Collaborator

Thanks for confirming. Ring API changes multiple times a year, it's always critical to keep the API updated to the latest released versions if you expect things to work correctly.

@Darrilla
Copy link
Author

Darrilla commented Feb 7, 2025

@tsightler As you know, we have had problems getting this library to work in our own system. We could run the test programs and it would work, but when run as part of our larger system, it would not work. Today, finally, we figured out why it wasn't working. In our main application (the one that executes 3rd-party integrations like ring), we for some reason were overriding the global fetch with 'node-fetch', which apparently by default reuses connections.

const fetch = require('node-fetch');
global.fetch = fetch;

By removing this override, now the new version of this ring-client-api lib works great within our application.
I believe that adding the header { connection: 'close' } would mitigate this, but I provide this information here in case others run into similar problems. I'm not sure this warrants any update to your library code, but I'll leave that up to you.

Thanks for your time and help as we worked through this problem.

@tsightler
Copy link
Collaborator

It definitely doesn't require an update as overriding a global capability with a completely alternate implementation would not be considered proper practice and it's completely unrealistic to expect 3rd party dependencies to work as expected in this case.

Thanks for sharing what you found for your case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Let's talk about it
Projects
None yet
Development

No branches or pull requests

4 participants