Skip to content

Commit

Permalink
python: added support for UDP sendto and TCP transport (cf. uhppoted/…
Browse files Browse the repository at this point in the history
  • Loading branch information
twystd committed Jul 22, 2024
1 parent 716fe4b commit d503560
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ python: build regen
chmod +x generated/python/main.py

python-debug: python
$(PYBIN) --debug --bind 192.168.1.100 --broadcast 192.168.1.255:60000 --listen 192.168.1.100:60001 set-door-passcodes
$(PYBIN) --debug --bind 192.168.1.100 --broadcast 192.168.1.255:60000 --listen 192.168.1.100:60001 get-controller

python-usage: python
$(PYBIN)
Expand Down
11 changes: 9 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@
- [ ] TCP/IP protocol (cf. https://github.com/uhppoted/uhppote-core/issues/17)
- [ ] models: rename 'device id' to controller and set type to 'controller'
- [ ] Go
- [x] udp-sendto
- [x] tcp-sendto
- [x] controllers dictionary
- [ ] remove uhppote::resolve
- [ ] Rust
- [x] udp-sendto
- [x] tcp-sendto
- [x] controllers dictionary
- [ ] remove uhppote::resolve
- [ ] Python
- [x] udp-sendto
- [x] tcp-sendto
- [x] controllers dictionary
- [ ] remove uhppote::resolve
- [ ] command line args
- [ ] Erlang
- [ ] HTTP
- [ ] Lua
- [ ] Python
- [ ] PHP
- [ ] Zig

Expand Down
13 changes: 12 additions & 1 deletion bindings/python/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
GATEWAY = ipaddress.IPv4Address('192.168.1.1')
LISTENER = (ipaddress.IPv4Address('192.168.1.100'), 60001)

CONTROLLERS = {
405419896: uhppote.Controller(405419896,'192.168.1.100:60000','tcp'),
}


def commands():
return {
Expand Down Expand Up @@ -77,7 +81,7 @@ def get_all_controllers(u):


def get_controller(u):
controller = CONTROLLER
controller = resolve(CONTROLLER)

return u.get_controller(controller)

Expand Down Expand Up @@ -363,3 +367,10 @@ def listen(u):
def onEvent(event):
if event != None:
pprint(event.__dict__, indent=2, width=1)

def resolve(controller):
v = CONTROLLERS.get(controller, None)
if not v is None:
return v
else:
return uhppote.Controller(controller, None, 'udp')
62 changes: 55 additions & 7 deletions bindings/python/udp.py → bindings/python/net.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
NO_TIMEOUT = struct.pack('ll', 0, 0)


class UDP:
class Net:

def __init__(self, bind='0.0.0.0', broadcast='255.255.255.255:60000', listen="0.0.0.0:60001", debug=False):
self._bind = (bind, 0)
Expand All @@ -34,13 +34,22 @@ def broadcast(self, request):
finally:
sock.close()

def send(self, request):
def send(self, controller, request):
self.dump(request)

# sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM | socket.SOCK_NONBLOCK)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
if controller.transport == 'tcp' and not controller.address is None and controller.address != '':
address = resolve(controller.address)

try:
return self.tcp_sendto(request, address)
elif not controller.address is None and controller.address != '':
address = resolve(controller.address)

return self.udp_sendto(request, address)
else:
return self.udp_broadcast_to(request)

def udp_broadcast_to(self, request):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) as sock:
sock.bind(self._bind)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, WRITE_TIMEOUT)
Expand All @@ -52,8 +61,36 @@ def send(self, request):
return None

return read(sock, self._debug)
finally:
sock.close()

def udp_sendto(self, request, address):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) as sock:
sock.bind(self._bind)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, WRITE_TIMEOUT)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, READ_TIMEOUT)

sock.sendto(request, address)

if request[1] == 0x96:
return None

return read(sock, self._debug)

def tcp_sendto(self, request, address):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, WRITE_TIMEOUT)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, READ_TIMEOUT)

if not is_INADDR_ANY(self._bind):
sock.bind(self._bind)

sock.connect(address)
sock.sendall(request)

if request[1] == 0x96:
return None
else:
return read(sock, self._debug)

def listen(self, events):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
Expand Down Expand Up @@ -116,6 +153,17 @@ def resolve(addr):
address = ipaddress.IPv4Address(addr)
return (str(address), 60000)

def is_INADDR_ANY(addr):
if addr == None:
return True

if f'{addr}' == '':
return True

if addr == (('0.0.0.0', 0)):
return True

return False

def dump(packet):
for i in range(0, 4):
Expand Down
40 changes: 32 additions & 8 deletions bindings/python/uhppote.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from collections import namedtuple

import encode
import decode
import udp
import net

Controller = namedtuple('Controller', 'controller address transport')

class Uhppote:
def __init__(self, bind='0.0.0.0', broadcast='255.255.255.255:60000', listen="0.0.0.0:60001", debug=False):
self._udp = udp.UDP(bind, broadcast, listen, debug)
self._net = net.Net(bind, broadcast, listen, debug)

def get_all_controllers(self):
request = encode.get_controller_request(0)
replies = self._udp.broadcast(request)
replies = self._net.broadcast(request)

list = []
for reply in replies:
Expand All @@ -18,7 +21,7 @@ def get_all_controllers(self):
return list

def listen(self, onEvent):
self._udp.listen(lambda packet: self.events(packet, onEvent))
self._net.listen(lambda packet: self.events(packet, onEvent))
return None


Expand All @@ -31,17 +34,38 @@ def events(self, packet, onEvent):

{{define "function"}}
def {{snakeCase .name}}(self, {{template "args" .args}}):
{{if .response}}request = encode.{{snakeCase .request.name}}({{template "params" .args}})
reply = self._udp.send(request)
c = resolve(controller)

{{if .response}}request = encode.{{snakeCase .request.name}}(c.controller{{template "params" slice .args 1}})
reply = self._net.send(c, request)

if reply != None:
return decode.{{snakeCase .response.name}}(reply)

return None
{{- else}}
request = encode.{{snakeCase .request.name}}({{template "params" .args}})
self._udp.send(request)
request = encode.{{snakeCase .request.name}}(c.controller, {{template "params" slice .args 1}})
self._net.send(c, request)

return True
{{end}}
{{end}}

def resolve(v):
if isinstance(v, int):
return Controller(v, None, 'udp')

if isinstance(v, tuple):
controller = v[0]
address = None
protocol = 'udp'

if len(v) > 1 and isinstance(v[1], str):
address = v[1]

if len(v) > 2 and (v[2] == 'tcp' or v[2] == 'TCP'):
protocol = 'tcp'

return Controller(controller, address, protocol)

return Controller(None, None, 'udp')

0 comments on commit d503560

Please sign in to comment.