Getting service info when a single server is removed #1235
Description
Hey everyone,
I note that the listener's add_service() callback has available to it the coordinates for the custom service that I am tracking. Whenever it comes online this callback can use get_service_info() to get the host and port.
I note that the listener's remove_service() callback cannot try to obtain the host and port of the service that just went offline by querying get_service_info(). The latter will return None.
My application would like to track a particular service and update a GUI accordingly. When the service comes online it's easy to add the host and port to some kind of GUI list, but I cannot figure out how to determine which server just went down when remove_service() callback is invoked.
How can I get this information?
Maybe I'm going about this all the wrong way, but presumably this is a common issue others have had in the past.
Activity
bdraco commentedon Sep 3, 2023
You need to watch for Removed callbacks. Check the examples
kiplingw commentedon Sep 3, 2023
Hey @bdraco. Thanks for responding. As I mentioned, as far as I understand it, the
remove_service()callback doesn't have enough information available to it to obtain the host and port of the service that just went down when it queriesget_service_info(). The latter will returnNone.I looked at the examples, but perhaps I'm missing something?
bdraco commentedon Sep 3, 2023
Once a service has been removed it’s info is gone and there is no responder on the network anymore to answer questions to fill ServiceInfo, you’ll have to save it when it’s added if you need it later
kiplingw commentedon Sep 3, 2023
No I get that, but even if you keep a list as each service is added, you don't know which one of them was removed when you get the notification - only that one of them has been.
bdraco commentedon Sep 3, 2023
The name is in the remove callback so you can keep a dict of the service info by name when it's added
kiplingw commentedon Sep 3, 2023
Yes, but as I mentioned I'm only tracking one type of custom service. So all of the add and remove notifications are for the same type of service, but not necessarily the same server.
bdraco commentedon Sep 3, 2023
You probably need to install your own RecordUpdateListener and watch records as they come in and are removed.
kiplingw commentedon Sep 3, 2023
Thanks @bdraco, but I'm still struggling. I looked up what I could find on
RecordUpdateListenerand I'm not clear if I need to change the following to derive from that class?bdraco commentedon Sep 3, 2023
Maybe this will help
https://github.com/esphome/aioesphomeapi/blob/4c992365e0b935d822a0126ca21bfd7d9e0b02d2/aioesphomeapi/reconnect_logic.py#L249
kiplingw commentedon Sep 3, 2023
I'm sorry but it doesn't. It's not clear what you're directing me to. This looks like a method of a class derived from
zeroconf.RecordUpdateListener? It's not clear to me whatasync_update_recordsis doing and how that relates to my problem.kiplingw commentedon Mar 25, 2024
👋🏽
bdraco commentedon Apr 1, 2024
You'll have to cache the data when the service is added or updated, as once it's removed, the device is off-line and will not respond to requests
kiplingw commentedon Apr 1, 2024
Right, but cache which data?
bdraco commentedon Apr 13, 2024
From your opening text, I expect you need to keep a map of service name to host/port and look it up in the dict when it goes offline
kiplingw commentedon Apr 13, 2024
@bdraco, as I mentioned above the remove notification doesn't tell you which host went offline. So even if you keep a cache of service, host, and port as they come online, you don't know which one of them went offline when they do. You're only given the name of the service.
1 remaining item
kiplingw commentedon Apr 13, 2024
That still doesn't address the problem. As I said above I have multiple machines advertising the same service on the network. Each machine has a different host and port. When the service is announced a listener sees the name of the service, its host, and port. When the service goes offline, the listener only knows that an instance of a service name is gone, but not which host and port.
bdraco commentedon Apr 13, 2024
Its a bit unconventional that you would have multiple different hosts published for the same service name that change frequently, but in that case you'll have to go with the
RecordUpdateListenerI originally suggested in #1235 (comment) and make anasync_update_recordswhich watches for a RecordUpdate to know when records to go away when they expire. You can check if the record is expired withis_expiredpython-zeroconf/src/zeroconf/_core.py
Line 569 in 9d8dd27
python-zeroconf/src/zeroconf/_updates.py
Line 52 in 9d8dd27
kiplingw commentedon Apr 14, 2024
Thanks @bdraco. I'll give it a go when I have time and report back.
sdbbs commentedon Apr 19, 2024
I just ran into this problem, I will try to illustrate.
Well, I have a work setup, where I have (let's say) 10 Raspberry Pis, each of which runs an OLA server. And also, let's say I want to keep track of all of them - whether their OLA servers are online - in a table.
So, I run my
class MyListener(ServiceListener)script, and have a corresponding printoutprint(f"add_service {name=} {type_=} {info=} {zc=}")orprint(f"remove_service {name=} {type_=} {info=} {zc=}")in their respective handlers (and of course, ainfo = zc.get_service_info(type_, name)before the printouts.So first I get this printout (formatting mine)
Great - so I have an OLA Server running on device with hostname
my-rpi-03, with IP addresslist(b'\xc0\xa8\x01\x0f') == [192, 168, 1, 15], listening on port 9090. So I can add this entry to my table:Soon afterwards, I get this printout:
Great again - so I also have an OLA Server running on device with hostname
my-rpi-04, with IP addresslist(b'\xc0\xa8\x01\x1b') == [192, 168, 1, 27], listening again on port 9090. So I can add also this entry to my table:Excellent - exactly what I want: a list of devices with their hostnames and IP addresses, offering the service I'm interested on, on a given port.
OK, now just for fun, I log in to
my-rpi-04overssh, and shut down the OLA server with:Soon thereafter, I receive the
remove_serviceprintout:Great - now I know that an OLA Server went offline - but which one? 192.168.1.15 or 192.168.1.27? Which entry do I remove from the table?
Looking at this:
... I still cannot tell whether this RecordUpdate will have some sort of a unique identifier, relating it to a given hostname + IP address + port combination? Because if there isn't such an identifier, it is useless for the kind of "table tracking" I've just described above. But I guess, I'll have to take a look ...
In any case - if worse comes to worse, - I guess I could live with the following workaround:
remove_serviceevent comes in, we don't know from whom; so we just delete the entire table of online servers, and then somehow restart the entire Zeroconf discovery process, and populate the table anew - at least, when this process is done, the table should be accurate. But is there a command to restart the Zeroconf discovery process gracefully (at least, more gracefully than e.g. deleting the whole MyListener object, and re-instantiating it again? or exiting the app and restarting it?)kiplingw commentedon Apr 19, 2024
Excellent illustration of the problem, @sdbbs. Thank you.
sdbbs commentedon Apr 19, 2024
OK, I found one workaround: given that in the context of this problem, we actually start by managing an online servers table, obviously, at first, we will get this table populated with entries advertising the service of interest (SOI) via
add_service.Then, let's say
remove_servicehits - as stated before, at this point, we do not know which device server went offlne; however, thanks to our initial goal to manage a table -- at this point, we do know which servers had previously been online, and we have a concrete, known, number of them in a list/table.So then, the solution is: when
remove_servicehits, loop through all entries of the (previously) online servers table, and check if the specified port at the specified IP address is open; when we arrive at a device in the online servers list that does not respond at the port any more, then it is this device('s server) that went offline, and is the source ofremove_serviceZeroconf event; and so, it is this device entry that should be removed from the online servers list.Here is an example I cooked up from the README example:
So I could only test with one RPi with OLA on the local network; but when I start the script with RPI OLA server online on local network, I get printouts:
Then I log in to the RPi, and shut down OLA server with
... and soon afterwards I get this printout:
For the kind of context I need this for (servers running on Raspberry Pis on local network, unencrypted) this seems to work - though I'm not sure if socket checking for open ports will work for HTTPS servers or similar .... so some more complicated "check open port" or "establish connection" or "ping" methods might be required there for this concept to work ...
This has not been extensively tested so YMMV, but at least with one device, - in principle - it seems to work.
kiplingw commentedon Apr 19, 2024
[-]Getting service info on unregistration[/-][+]Getting service info when a server is renamed[/+][-]Getting service info when a server is renamed[/-][+]Getting service info when a server is removed[/+][-]Getting service info when a server is removed[/-][+]Getting service info when a single server is removed[/+]bdraco commentedon Feb 15, 2025
I think what you are asking for is a new type of browser aka ServerBrowser
Assuming the following
This could probably be implemented as a new browser object that in addition a to the service changes (PTR records) you could get a separate callback for when a server is added,remove,changed (including the A/AAAA) records
Assuming all the above, this is a bit of a large project that should take about 2-3 weeks to properly build and test.
kiplingw commentedon Feb 15, 2025
Thanks @bdraco. I'd love to contribute, but I'm not an expert in the library. However, I'd be happy to test a PR.