forked from platypush/platypush
Added TCP and UDP plugins - closes #106
This commit is contained in:
parent
c3024fba2d
commit
d73df1454e
5 changed files with 189 additions and 0 deletions
5
docs/source/platypush/plugins/tcp.rst
Normal file
5
docs/source/platypush/plugins/tcp.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``platypush.plugins.tcp``
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.tcp
|
||||||
|
:members:
|
5
docs/source/platypush/plugins/udp.rst
Normal file
5
docs/source/platypush/plugins/udp.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``platypush.plugins.udp``
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.udp
|
||||||
|
:members:
|
|
@ -85,11 +85,13 @@ Plugins
|
||||||
platypush/plugins/switch.rst
|
platypush/plugins/switch.rst
|
||||||
platypush/plugins/switch.switchbot.rst
|
platypush/plugins/switch.switchbot.rst
|
||||||
platypush/plugins/switch.wemo.rst
|
platypush/plugins/switch.wemo.rst
|
||||||
|
platypush/plugins/tcp.rst
|
||||||
platypush/plugins/todoist.rst
|
platypush/plugins/todoist.rst
|
||||||
platypush/plugins/torrent.rst
|
platypush/plugins/torrent.rst
|
||||||
platypush/plugins/trello.rst
|
platypush/plugins/trello.rst
|
||||||
platypush/plugins/tts.rst
|
platypush/plugins/tts.rst
|
||||||
platypush/plugins/tts.google.rst
|
platypush/plugins/tts.google.rst
|
||||||
|
platypush/plugins/udp.rst
|
||||||
platypush/plugins/user.rst
|
platypush/plugins/user.rst
|
||||||
platypush/plugins/utils.rst
|
platypush/plugins/utils.rst
|
||||||
platypush/plugins/variable.rst
|
platypush/plugins/variable.rst
|
||||||
|
|
119
platypush/plugins/tcp.py
Normal file
119
platypush/plugins/tcp.py
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
class TcpPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Plugin for raw TCP communications.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self._sockets = {}
|
||||||
|
|
||||||
|
def _connect(self, host: str, port: int, timeout: Optional[float] = None) -> socket.socket:
|
||||||
|
sd = self._sockets.get((host, port))
|
||||||
|
if sd:
|
||||||
|
return sd
|
||||||
|
|
||||||
|
sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
if timeout:
|
||||||
|
sd.settimeout(timeout)
|
||||||
|
sd.connect((host, port))
|
||||||
|
self._sockets[(host, port)] = sd
|
||||||
|
return sd
|
||||||
|
|
||||||
|
@action
|
||||||
|
def connect(self, host: str, port: int, timeout: Optional[float] = None):
|
||||||
|
"""
|
||||||
|
Open a TCP connection.
|
||||||
|
|
||||||
|
:param host: Host IP/name.
|
||||||
|
:param port: TCP port.
|
||||||
|
:param timeout: Connection timeout in seconds (default: None).
|
||||||
|
"""
|
||||||
|
self._connect(host, port, timeout)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def close(self, host: str, port: int):
|
||||||
|
"""
|
||||||
|
Close an active TCP connection.
|
||||||
|
|
||||||
|
:param host: Host IP/name.
|
||||||
|
:param port: TCP port.
|
||||||
|
"""
|
||||||
|
sd = self._sockets.get((host, port))
|
||||||
|
if not sd:
|
||||||
|
self.logger.warning('Not connected to ({}, {})'.format(host, port))
|
||||||
|
return
|
||||||
|
|
||||||
|
sd.close()
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send(self, data: Union[bytes, str], host: str, port: int, binary: bool = False,
|
||||||
|
timeout: Optional[float] = None, recv_response: bool = False, **recv_opts):
|
||||||
|
"""
|
||||||
|
Send data over a TCP connection. If the connection isn't active it will be created.
|
||||||
|
|
||||||
|
:param data: Data to be sent, as bytes or string.
|
||||||
|
:param host: Host IP/name.
|
||||||
|
:param port: TCP port.
|
||||||
|
:param binary: If set to True and ``data`` is a string then will be treated as base64-encoded binary input.
|
||||||
|
:param timeout: Connection timeout in seconds (default: None).
|
||||||
|
:param recv_response: If True then the action will wait for a response from the server before closing the
|
||||||
|
connection. Note that ``recv_opts`` must be specified in this case - at least ``length``.
|
||||||
|
"""
|
||||||
|
if isinstance(data, list) or isinstance(data, dict):
|
||||||
|
data = json.dumps(data)
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = data.encode()
|
||||||
|
if binary:
|
||||||
|
data = base64.decodebytes(data)
|
||||||
|
|
||||||
|
sd = self._connect(host, port, timeout)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sd.send(data)
|
||||||
|
if recv_response:
|
||||||
|
recv_opts.update({
|
||||||
|
'host': host,
|
||||||
|
'port': port,
|
||||||
|
'timeout': timeout,
|
||||||
|
'binary': binary,
|
||||||
|
})
|
||||||
|
|
||||||
|
return self.recv(**recv_opts)
|
||||||
|
finally:
|
||||||
|
self.close(host, port)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def recv(self, length: int, host: str, port: int, binary: bool = False, timeout: Optional[float] = None) -> str:
|
||||||
|
"""
|
||||||
|
Receive data from a TCP connection. If the connection isn't active it will be created.
|
||||||
|
|
||||||
|
:param length: Maximum number of bytes to be received.
|
||||||
|
:param host: Host IP/name.
|
||||||
|
:param port: TCP port.
|
||||||
|
:param binary: If set to True then the output will be base64-encoded, otherwise decoded as string.
|
||||||
|
:param timeout: Connection timeout in seconds (default: None).
|
||||||
|
"""
|
||||||
|
sd = self._connect(host, port, timeout)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = sd.recv(length)
|
||||||
|
if binary:
|
||||||
|
data = base64.encodebytes(data).decode()
|
||||||
|
else:
|
||||||
|
data = data.decode()
|
||||||
|
|
||||||
|
return data
|
||||||
|
finally:
|
||||||
|
self.close(host, port)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
58
platypush/plugins/udp.py
Normal file
58
platypush/plugins/udp.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
class UdpPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Plugin for raw UDP communications.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@action
|
||||||
|
def send(self, data: Union[bytes, str], host: str, port: int, binary: bool = False,
|
||||||
|
timeout: Optional[float] = None, recv_response: bool = False, **recv_opts):
|
||||||
|
"""
|
||||||
|
Send data over a UDP connection.
|
||||||
|
|
||||||
|
:param data: Data to be sent, as bytes or string.
|
||||||
|
:param host: Host IP/name.
|
||||||
|
:param port: TCP port.
|
||||||
|
:param binary: If set to True and ``data`` is a string then will be treated as base64-encoded binary input.
|
||||||
|
:param timeout: Connection timeout in seconds (default: None).
|
||||||
|
:param recv_response: If True then the action will wait for a response from the server before closing the
|
||||||
|
connection. Note that ``recv_opts`` must be specified in this case - at least ``length``.
|
||||||
|
"""
|
||||||
|
if isinstance(data, list) or isinstance(data, dict):
|
||||||
|
data = json.dumps(data)
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = data.encode()
|
||||||
|
if binary:
|
||||||
|
data = base64.decodebytes(data)
|
||||||
|
|
||||||
|
sd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
if timeout:
|
||||||
|
sd.settimeout(timeout)
|
||||||
|
|
||||||
|
sd.sendto(data, (host, port))
|
||||||
|
if not recv_response:
|
||||||
|
return
|
||||||
|
|
||||||
|
recv_opts.update({
|
||||||
|
'host': host,
|
||||||
|
'port': port,
|
||||||
|
'timeout': timeout,
|
||||||
|
'binary': binary,
|
||||||
|
})
|
||||||
|
|
||||||
|
data = sd.recvfrom(**recv_opts)
|
||||||
|
if binary:
|
||||||
|
data = base64.encodebytes(data)
|
||||||
|
data = data.decode()
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
Loading…
Reference in a new issue