When run under Python 2, the zeroconf _octoprint._tcp.local gets announced with 127.0.0.1 as the first address, which apparently causes the Cura OctoPrint Connection plugin to not be able to see it. On Python 3, things work as expected.
$ python zeroconf_browser.py
Press enter to exit...
ADDED: OctoPrint instance "Prusa MK3"._octoprint._tcp.local., 127.0.0.1:80
ServiceInfo(type='_octoprint._tcp.local.', name='OctoPrint instance "Prusa MK3"._octoprint._tcp.local.', addresses=[b'\x7f\x00\x00\x01', b'\xc0\xa8\x018'], port=80, weight=0, priority=0, server='prusamk3.local.', properties={b'path': b'/', b'api': b'0.1', b'uuid': b'bb16bdd7-7864-4d53-8a3f-61ccdcd9f9c7', b'version': b'1.5.0rc1'})
ADDED: OctoPrint instance on octopiB._octoprint._tcp.local., 127.0.0.1:80
ServiceInfo(type='_octoprint._tcp.local.', name='OctoPrint instance on octopiB._octoprint._tcp.local.', addresses=[b'\x7f\x00\x00\x01', b'\xc0\xa8\x01\x99'], port=80, weight=0, priority=0, server='octopiB.local.', properties={b'path': b'/', b'api': b'0.1', b'uuid': b'8b2ac37e-b50e-415d-8556-67fdf1c8b726', b'version': b'1.5.0rc1'})
ADDED: OctoPrint instance on octopiA._octoprint._tcp.local., 127.0.0.1:80
ServiceInfo(type='_octoprint._tcp.local.', name='OctoPrint instance on octopiA._octoprint._tcp.local.', addresses=[b'\x7f\x00\x00\x01', b'\xc0\xa8\x01\xb8'], port=80, weight=0, priority=0, server='octopiA.local.', properties={b'path': b'/', b'api': b'0.1', b'uuid': b'0cbc7989-71fb-44cb-9ae7-bea10cc81c32', b'version': b'1.5.0rc1'})
ADDED: OctoPrint instance "Ender 3 Pro"._octoprint._tcp.local., 192.168.1.57:80
ServiceInfo(type='_octoprint._tcp.local.', name='OctoPrint instance "Ender 3 Pro"._octoprint._tcp.local.', addresses=[b'\xc0\xa8\x019'], port=80, weight=0, priority=0, server='ender3pro.local.', properties={b'path': b'/', b'version': b'1.5.0rc1', b'api': b'0.1', b'uuid': b'ffa23bac-ec2a-4fb9-8242-20d6c92d1a7a'})
In the above example, "Prusa MK3", "octopiA" and "octopiB" all run under Py2, "Ender 3 Pro" runs under Py3.
The difference between the two is that under Py2 we use a bundled version of zeroconf, on Py3 we use the official version. There might be some patch missing that needs to be found and applied.
For reference, zeroconf_browser.py, based on zeroconf's own example:
from zeroconf import ServiceBrowser, Zeroconf
import sys
import socket
class MyListener(object):
def remove_service(self, zeroconf, type, name):
print("REMOVED: {}\n".format(name))
def add_service(self, zeroconf, type, name):
info = zeroconf.get_service_info(type, name)
print("ADDED: {}, {}:{}\n{}\n".format(name, socket.inet_ntoa(info.address), info.port, info))
if len(sys.argv) == 1:
service = "_octoprint._tcp.local."
else:
service = sys.argv[1]
if not service.endswith("."):
service += "."
if not service.endswith("local."):
service += "local."
zeroconf = Zeroconf()
listener = MyListener()
browser = ServiceBrowser(zeroconf, service, listener)
try:
input("Press enter to exit...\n\n")
finally:
zeroconf.close()
Different behaviour of octoprint.util.net.interface_addresses() can be ruled out as the cause, as on both systems they return 127.0.0.1 AND 192.168.1.x address:
Python 2.7.13 (default, Sep 26 2018, 18:42:22)
[GCC 6.3.0 20170516] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import octoprint.util.net
>>> list(octoprint.util.net.interface_addresses())
[u'127.0.0.1', u'192.168.1.56']
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import octoprint.util.net
>>> list(octoprint.util.net.interface_addresses())
['127.0.0.1', '192.168.1.57']
You can also blame the Cura plugin. I thought I made it ignore the 127.0.0.1 address if there were another one listed.
Ah, I was mistaken; I'm only ignoring link_local addresses (169.254.x.x):
https://github.com/fieldOfView/Cura-OctoPrintPlugin/blob/3.5/OctoPrintOutputDevicePlugin.py#L308
Giving this some more thought: advertising localhost over the network is probably not a good idea in the first place, so I blame you ;-)
I'm taking full blame :P I'd just like to understand where the py3 version goes differently than the py2 version, haven't found that yet. Would be easy to just filter out localhost addresses, but if there's differences in behaviour like this, maybe there are more...
Just to understand, are you simply using the first listed address that's left over after filtering?
are you simply using the first listed address that's left over after filtering?
Looks like it, yes.
Hm, it's sadly more than just the address being the issue here...
MDNS response generated by the Py3 instance:

MDNS response generated by the Py2 instance:

All of the additional records are missing. Weirdly enough, the same code on my local development instance (currently also switched to Python 2) produces a full record (and also shows up in Cura). This is going to be fun
Turns out that was actually the core of the issue. Current zeroconf responds to PTR queries according to RFC6763, so with a SRV, a TXT and as many A or AAAA records as needed to communicate all addresses. Bundled version that was based on the latest version still compatible to Py2 didn't.
Backported, fix ready for 1.5.0rc2
1.5.0rc2 is out