zeroconnect


Namezeroconnect JSON
Version 1.0.1 PyPI version JSON
download
home_page
SummaryUse zeroconf to automatically connect devices via TCP on a LAN
upload_time2022-12-07 23:40:32
maintainer
docs_urlNone
author
requires_python>=3.7
license
keywords automagic automatic connect iot mesh network tcp zeroconf
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # zeroconnect

[![PyPI - Version](https://img.shields.io/pypi/v/zeroconnect.svg)](https://pypi.org/project/zeroconnect)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/zeroconnect.svg)](https://pypi.org/project/zeroconnect)

Use zeroconf to automatically connect devices via TCP on a LAN.
I can hardly believe this doesn't exist already, but after searching for an hour, in despair I resign myself to write my own, and patch the glaring hole in existence.

-----

**Table of Contents**

- [Installation](#installation)
- [Usage](#usage)
- [License](#license)
- [Tips](#tips)
- [Bonus!](#bonus)

## Installation

```console
pip install zeroconnect
```

## Usage

One or more servers, and one or more clients, run connected to the same LAN.  (Wifi or ethernet.)

### Most basic

Service:
```python
from zeroconnect import ZeroConnect

def rxMessageConnection(messageSock, nodeId, serviceId):
    print(f"got message connection from {nodeId}")
    data = messageSock.recvMsg()
    print(data)
    messageSock.sendMsg(b"Hello from server")

ZeroConnect().advertise(rxMessageConnection, "YOUR_SERVICE_ID_HERE")
```

Client:
```python
from zeroconnect import ZeroConnect
messageSock = ZeroConnect().connectToFirst("YOUR_SERVICE_ID_HERE")
messageSock.sendMsg(b"Hello from client")
data = messageSock.recvMsg()
print(data)
```

### Less basic

Service:
```python
from zeroconnect import ZeroConnect

SERVICE_ID = "YOUR_SERVICE_ID_HERE"
zc = ZeroConnect("NODE_ID")

def rxMessageConnection(messageSock, nodeId, serviceId):
    print(f"got message connection from {nodeId}")
    # If you also want to spontaneously send messages, pass the socket to e.g. another thread.
    while True:
        data = messageSock.recvMsg()
        print(data)
        if data == b'enable jimjabber':
            print(f"ENABLE JIMJABBER")
        elif data == b'save msg:':
            toSave = messageSock.recvMsg()
            print(f"SAVE MESSAGE {toSave}")
        elif data == b'marco':
            messageSock.sendMsg(b'polo')
            print(f"PING PONGED")
        elif data == None:
            print(f"Connection closed from {nodeId}")
            messageSock.close()
            return
        else:
            print(f"Unhandled message: {data}")
        # Use messageSock.sock for e.g. sock.getsockname()
        # I recommend messageSock.close() after you're done with it - but it'll get closed on zc.close(), at least

zc.advertise(rxMessageConnection, SERVICE_ID) # Implicit mode=SocketMode.Messages

try:
    input("Press enter to exit...\n\n")
finally:
    zc.close()
```

Client:
```python
from zeroconnect import ZeroConnect, SocketMode

SERVICE_ID = "YOUR_SERVICE_ID_HERE"
zc = ZeroConnect("NODE_ID") # Technically the nodeId is optional; it'll assign you a random UUID

ads = zc.scan(SERVICE_ID, time=5)
# OR: ads = zc.scan(SERVICE_ID, NODE_ID)
# An `Ad` contains a `serviceId` and `nodeId` etc.; see `Ad` for details
messageSock = zc.connect(ads[0], mode=SocketMode.Messages) # Send and receive messages; the default mode
# OR: messageSock = zc.connectToFirst(SERVICE_ID)
# OR: messageSock = zc.connectToFirst(nodeId=NODE_ID)
# OR: messageSock = zc.connectToFirst(SERVICE_ID, NODE_ID, timeout=10)

messageSock.sendMsg(b"enable jimjabber")
messageSock.sendMsg(b"save msg:")
messageSock.sendMsg(b"i love you")
messageSock.sendMsg(b"marco")
print(f"rx: {messageSock.recvMsg()}")

# ...

zc.close()
```

You can also get raw sockets rather than MessageSockets, if you prefer:

Server:
```python
from zeroconnect import ZeroConnect, SocketMode

SERVICE_ID = "YOUR_SERVICE_ID_HERE"
zc = ZeroConnect("NODE_ID")

def rxRawConnection(sock, nodeId, serviceId):
    print(f"got raw connection from {nodeId}")
    data = sock.recv(1024)
    print(data)
    sock.sendall(b"Hello from server\n")
    # sock is a plain socket; use accordingly

zc.advertise(rxRawConnection, SERVICE_ID, mode=SocketMode.Raw)

try:
    input("Press enter to exit...\n\n")
finally:
    zc.close()
```

Client:
```python
from zeroconnect import ZeroConnect, SocketMode

SERVICE_ID = "YOUR_SERVICE_ID_HERE"
zc = ZeroConnect("NODE_ID") # Technically the nodeId is optional; it'll assign you a random UUID

ads = zc.scan(SERVICE_ID, time=5)
# OR: ads = zc.scan(SERVICE_ID, NODE_ID)
# An `Ad` contains a `serviceId` and `nodeId` etc.; see `Ad` for details
sock = zc.connect(ads[0], mode=SocketMode.Raw) # Get the raw streams
# OR: sock = zc.connectToFirst(SERVICE_ID, mode=SocketMode.Raw)
# OR: sock = zc.connectToFirst(nodeId=NODE_ID, mode=SocketMode.Raw)
# OR: sock = zc.connectToFirst(SERVICE_ID, NODE_ID, mode=SocketMode.Raw, timeout=10)

sock.sendall(b"Hello from client\n")
data = sock.recv(1024)
print(f"rx: {data}")

# ...

zc.close()
```

There's a few other functions you might find useful.  Look at the source code.
Here; I'll paste the declaration of all the public `ZeroConnect` methods here.

```python
    def __init__(self, localId=None):
    def advertise(self, callback, serviceId, port=0, host="0.0.0.0", mode=SocketMode.Messages):
    def scan(self, serviceId=None, nodeId=None, time=30):
    def scanGen(self, serviceId=None, nodeId=None, time=30):
    def connectToFirst(self, serviceId=None, nodeId=None, localServiceId="", mode=SocketMode.Messages, timeout=30):
    def connect(self, ad, localServiceId="", mode=SocketMode.Messages):
    def broadcast(self, message, serviceId=None, nodeId=None):
    def getConnections(self):
    def close(self):
```

## Tips

Be careful not to have two nodes recv from each other at the same time, or they'll deadlock.
However, you CAN have them send at the same time (at least according to my tests).

`ZeroConnect` is intended to be manipulated via its methods, but it probably won't immediately explode if you
read the data in the fields.

Note that some computers/networks block zeroconf, or external connection attempts, etc.

Calling `broadcast` will automatically clean up dead connections.

If you close your socket immediately after sending a message, the data may not finish sending.  Not my fault; blame socket.

`broadcast` uses MessageSockets, so if you're using a raw socket, be aware the message will be prefixed with a header, currently
an 8 byte unsigned long representing the length of the subsequent message.  See `MessageSocket`.

See logging.py to see logging settings, or do like so:
```python
from zeroconnect.logging import *
setLogLevel(-1) # 4+ for everything current, -1 for nothing except uncaught exceptions
# It also contains some presets; ERROR/WARN/INFO/VERBOSE/DEBUG atm.
# Also, you can move all the logging to stderr with `setLogType(2)`.
```

## Bonus!

Also includes zcat, like ncat/nc/netcat.  Use as follows:

RX:
```bash
python -m zeroconnect.zcat -l SERVICE_ID [NODE_ID] > FILE
```

TX:
```bash
cat FILE | python -m zeroconnect.zcat SERVICE_ID [NODE_ID]
```


## License

`zeroconnect` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.

## TODO
ssl
lower timeouts?
connect to all, forever?
    connection callback
maybe some automated tests?
.advertiseSingle to get one connection?  for quick stuff?

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "zeroconnect",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "automagic,automatic,connect,iot,mesh,network,tcp,zeroconf",
    "author": "",
    "author_email": "Erhannis Kirran <eyeillus@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/10/15/c2ef16fc6b2baec7c2886d17faae850cc536cf382795aa3da2c03afe10f0/zeroconnect-1.0.1.tar.gz",
    "platform": null,
    "description": "# zeroconnect\n\n[![PyPI - Version](https://img.shields.io/pypi/v/zeroconnect.svg)](https://pypi.org/project/zeroconnect)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/zeroconnect.svg)](https://pypi.org/project/zeroconnect)\n\nUse zeroconf to automatically connect devices via TCP on a LAN.\nI can hardly believe this doesn't exist already, but after searching for an hour, in despair I resign myself to write my own, and patch the glaring hole in existence.\n\n-----\n\n**Table of Contents**\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [License](#license)\n- [Tips](#tips)\n- [Bonus!](#bonus)\n\n## Installation\n\n```console\npip install zeroconnect\n```\n\n## Usage\n\nOne or more servers, and one or more clients, run connected to the same LAN.  (Wifi or ethernet.)\n\n### Most basic\n\nService:\n```python\nfrom zeroconnect import ZeroConnect\n\ndef rxMessageConnection(messageSock, nodeId, serviceId):\n    print(f\"got message connection from {nodeId}\")\n    data = messageSock.recvMsg()\n    print(data)\n    messageSock.sendMsg(b\"Hello from server\")\n\nZeroConnect().advertise(rxMessageConnection, \"YOUR_SERVICE_ID_HERE\")\n```\n\nClient:\n```python\nfrom zeroconnect import ZeroConnect\nmessageSock = ZeroConnect().connectToFirst(\"YOUR_SERVICE_ID_HERE\")\nmessageSock.sendMsg(b\"Hello from client\")\ndata = messageSock.recvMsg()\nprint(data)\n```\n\n### Less basic\n\nService:\n```python\nfrom zeroconnect import ZeroConnect\n\nSERVICE_ID = \"YOUR_SERVICE_ID_HERE\"\nzc = ZeroConnect(\"NODE_ID\")\n\ndef rxMessageConnection(messageSock, nodeId, serviceId):\n    print(f\"got message connection from {nodeId}\")\n    # If you also want to spontaneously send messages, pass the socket to e.g. another thread.\n    while True:\n        data = messageSock.recvMsg()\n        print(data)\n        if data == b'enable jimjabber':\n            print(f\"ENABLE JIMJABBER\")\n        elif data == b'save msg:':\n            toSave = messageSock.recvMsg()\n            print(f\"SAVE MESSAGE {toSave}\")\n        elif data == b'marco':\n            messageSock.sendMsg(b'polo')\n            print(f\"PING PONGED\")\n        elif data == None:\n            print(f\"Connection closed from {nodeId}\")\n            messageSock.close()\n            return\n        else:\n            print(f\"Unhandled message: {data}\")\n        # Use messageSock.sock for e.g. sock.getsockname()\n        # I recommend messageSock.close() after you're done with it - but it'll get closed on zc.close(), at least\n\nzc.advertise(rxMessageConnection, SERVICE_ID) # Implicit mode=SocketMode.Messages\n\ntry:\n    input(\"Press enter to exit...\\n\\n\")\nfinally:\n    zc.close()\n```\n\nClient:\n```python\nfrom zeroconnect import ZeroConnect, SocketMode\n\nSERVICE_ID = \"YOUR_SERVICE_ID_HERE\"\nzc = ZeroConnect(\"NODE_ID\") # Technically the nodeId is optional; it'll assign you a random UUID\n\nads = zc.scan(SERVICE_ID, time=5)\n# OR: ads = zc.scan(SERVICE_ID, NODE_ID)\n# An `Ad` contains a `serviceId` and `nodeId` etc.; see `Ad` for details\nmessageSock = zc.connect(ads[0], mode=SocketMode.Messages) # Send and receive messages; the default mode\n# OR: messageSock = zc.connectToFirst(SERVICE_ID)\n# OR: messageSock = zc.connectToFirst(nodeId=NODE_ID)\n# OR: messageSock = zc.connectToFirst(SERVICE_ID, NODE_ID, timeout=10)\n\nmessageSock.sendMsg(b\"enable jimjabber\")\nmessageSock.sendMsg(b\"save msg:\")\nmessageSock.sendMsg(b\"i love you\")\nmessageSock.sendMsg(b\"marco\")\nprint(f\"rx: {messageSock.recvMsg()}\")\n\n# ...\n\nzc.close()\n```\n\nYou can also get raw sockets rather than MessageSockets, if you prefer:\n\nServer:\n```python\nfrom zeroconnect import ZeroConnect, SocketMode\n\nSERVICE_ID = \"YOUR_SERVICE_ID_HERE\"\nzc = ZeroConnect(\"NODE_ID\")\n\ndef rxRawConnection(sock, nodeId, serviceId):\n    print(f\"got raw connection from {nodeId}\")\n    data = sock.recv(1024)\n    print(data)\n    sock.sendall(b\"Hello from server\\n\")\n    # sock is a plain socket; use accordingly\n\nzc.advertise(rxRawConnection, SERVICE_ID, mode=SocketMode.Raw)\n\ntry:\n    input(\"Press enter to exit...\\n\\n\")\nfinally:\n    zc.close()\n```\n\nClient:\n```python\nfrom zeroconnect import ZeroConnect, SocketMode\n\nSERVICE_ID = \"YOUR_SERVICE_ID_HERE\"\nzc = ZeroConnect(\"NODE_ID\") # Technically the nodeId is optional; it'll assign you a random UUID\n\nads = zc.scan(SERVICE_ID, time=5)\n# OR: ads = zc.scan(SERVICE_ID, NODE_ID)\n# An `Ad` contains a `serviceId` and `nodeId` etc.; see `Ad` for details\nsock = zc.connect(ads[0], mode=SocketMode.Raw) # Get the raw streams\n# OR: sock = zc.connectToFirst(SERVICE_ID, mode=SocketMode.Raw)\n# OR: sock = zc.connectToFirst(nodeId=NODE_ID, mode=SocketMode.Raw)\n# OR: sock = zc.connectToFirst(SERVICE_ID, NODE_ID, mode=SocketMode.Raw, timeout=10)\n\nsock.sendall(b\"Hello from client\\n\")\ndata = sock.recv(1024)\nprint(f\"rx: {data}\")\n\n# ...\n\nzc.close()\n```\n\nThere's a few other functions you might find useful.  Look at the source code.\nHere; I'll paste the declaration of all the public `ZeroConnect` methods here.\n\n```python\n    def __init__(self, localId=None):\n    def advertise(self, callback, serviceId, port=0, host=\"0.0.0.0\", mode=SocketMode.Messages):\n    def scan(self, serviceId=None, nodeId=None, time=30):\n    def scanGen(self, serviceId=None, nodeId=None, time=30):\n    def connectToFirst(self, serviceId=None, nodeId=None, localServiceId=\"\", mode=SocketMode.Messages, timeout=30):\n    def connect(self, ad, localServiceId=\"\", mode=SocketMode.Messages):\n    def broadcast(self, message, serviceId=None, nodeId=None):\n    def getConnections(self):\n    def close(self):\n```\n\n## Tips\n\nBe careful not to have two nodes recv from each other at the same time, or they'll deadlock.\nHowever, you CAN have them send at the same time (at least according to my tests).\n\n`ZeroConnect` is intended to be manipulated via its methods, but it probably won't immediately explode if you\nread the data in the fields.\n\nNote that some computers/networks block zeroconf, or external connection attempts, etc.\n\nCalling `broadcast` will automatically clean up dead connections.\n\nIf you close your socket immediately after sending a message, the data may not finish sending.  Not my fault; blame socket.\n\n`broadcast` uses MessageSockets, so if you're using a raw socket, be aware the message will be prefixed with a header, currently\nan 8 byte unsigned long representing the length of the subsequent message.  See `MessageSocket`.\n\nSee logging.py to see logging settings, or do like so:\n```python\nfrom zeroconnect.logging import *\nsetLogLevel(-1) # 4+ for everything current, -1 for nothing except uncaught exceptions\n# It also contains some presets; ERROR/WARN/INFO/VERBOSE/DEBUG atm.\n# Also, you can move all the logging to stderr with `setLogType(2)`.\n```\n\n## Bonus!\n\nAlso includes zcat, like ncat/nc/netcat.  Use as follows:\n\nRX:\n```bash\npython -m zeroconnect.zcat -l SERVICE_ID [NODE_ID] > FILE\n```\n\nTX:\n```bash\ncat FILE | python -m zeroconnect.zcat SERVICE_ID [NODE_ID]\n```\n\n\n## License\n\n`zeroconnect` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.\n\n## TODO\nssl\nlower timeouts?\nconnect to all, forever?\n    connection callback\nmaybe some automated tests?\n.advertiseSingle to get one connection?  for quick stuff?\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Use zeroconf to automatically connect devices via TCP on a LAN",
    "version": "1.0.1",
    "split_keywords": [
        "automagic",
        "automatic",
        "connect",
        "iot",
        "mesh",
        "network",
        "tcp",
        "zeroconf"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "6e9680a29f3cbbe8be002c1ebb932f71",
                "sha256": "6420208976139c11be0e1bd594206418e74d9d62de6d67b5f4e1ce3a7c4a4a51"
            },
            "downloads": -1,
            "filename": "zeroconnect-1.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6e9680a29f3cbbe8be002c1ebb932f71",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 15232,
            "upload_time": "2022-12-07T23:40:25",
            "upload_time_iso_8601": "2022-12-07T23:40:25.671421Z",
            "url": "https://files.pythonhosted.org/packages/9f/c2/663d79d5c656018abb0a3549c619590740e347777835e1f0e70ac01b3183/zeroconnect-1.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "39679c6820aeb90876d6ff8ce43abb5c",
                "sha256": "0a9883785a6451c69fad8c808c5302be1d15fba8cf4a4c2ccfd0a4e911eedee3"
            },
            "downloads": -1,
            "filename": "zeroconnect-1.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "39679c6820aeb90876d6ff8ce43abb5c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 13563,
            "upload_time": "2022-12-07T23:40:32",
            "upload_time_iso_8601": "2022-12-07T23:40:32.825332Z",
            "url": "https://files.pythonhosted.org/packages/10/15/c2ef16fc6b2baec7c2886d17faae850cc536cf382795aa3da2c03afe10f0/zeroconnect-1.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2022-12-07 23:40:32",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "zeroconnect"
}
        
Elapsed time: 0.01535s