Skip to content

Commit

Permalink
Merge 91075e1 into 3f99ae2
Browse files Browse the repository at this point in the history
  • Loading branch information
rnixx committed Jun 20, 2018
2 parents 3f99ae2 + 91075e1 commit 36f9a2d
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 10 deletions.
23 changes: 18 additions & 5 deletions xknx/io/knxip_interface.py
Expand Up @@ -36,6 +36,8 @@ class ConnectionConfig:
* local_ip: Local ip of the interface though which KNXIPInterface should connect.
* gateway_ip: IP of KNX/IP tunneling device.
* gateway_port: Port of KNX/IP tunneling device.
* auto_reconnect: Auto reconnect to KNX/IP tunneling device if connection cannot be established.
* auto_reconnect_wait: Wait n seconds before trying to reconnect to KNX/IP tunneling device.
"""

# pylint: disable=too-few-public-methods
Expand All @@ -44,12 +46,16 @@ def __init__(self,
connection_type=ConnectionType.AUTOMATIC,
local_ip=None,
gateway_ip=None,
gateway_port=DEFAULT_MCAST_PORT):
gateway_port=DEFAULT_MCAST_PORT,
auto_reconnect=False,
auto_reconnect_wait=3):
"""Initialize ConnectionConfig class."""
self.connection_type = connection_type
self.local_ip = local_ip
self.gateway_ip = gateway_ip
self.gateway_port = gateway_port
self.auto_reconnect = auto_reconnect
self.auto_reconnect_wait = auto_reconnect_wait


class KNXIPInterface():
Expand All @@ -72,7 +78,9 @@ async def start(self):
await self.start_tunnelling(
self.connection_config.local_ip,
self.connection_config.gateway_ip,
self.connection_config.gateway_port)
self.connection_config.gateway_port,
self.connection_config.auto_reconnect,
self.connection_config.auto_reconnect_wait)

async def start_automatic(self):
"""Start GatewayScanner and connect to the found device."""
Expand All @@ -86,11 +94,14 @@ async def start_automatic(self):
if gatewayscanner.supports_tunneling:
await self.start_tunnelling(gatewayscanner.found_local_ip,
gatewayscanner.found_ip_addr,
gatewayscanner.found_port)
gatewayscanner.found_port,
self.connection_config.auto_reconnect,
self.connection_config.auto_reconnect_wait)
elif gatewayscanner.supports_routing:
await self.start_routing(gatewayscanner.found_local_ip)

async def start_tunnelling(self, local_ip, gateway_ip, gateway_port):
async def start_tunnelling(self, local_ip, gateway_ip, gateway_port,
auto_reconnect, auto_reconnect_wait):
"""Start KNX/IP tunnel."""
self.xknx.logger.debug("Starting tunnel to %s:%s from %s", gateway_ip, gateway_port, local_ip)
self.interface = Tunnel(
Expand All @@ -99,7 +110,9 @@ async def start_tunnelling(self, local_ip, gateway_ip, gateway_port):
local_ip=local_ip,
gateway_ip=gateway_ip,
gateway_port=gateway_port,
telegram_received_callback=self.telegram_received)
telegram_received_callback=self.telegram_received,
auto_reconnect=auto_reconnect,
auto_reconnect_wait=auto_reconnect_wait)
await self.interface.start()

async def start_routing(self, local_ip):
Expand Down
52 changes: 47 additions & 5 deletions xknx/io/tunnel.py
Expand Up @@ -21,7 +21,9 @@ class Tunnel():

# pylint: disable=too-many-instance-attributes

def __init__(self, xknx, src_address, local_ip, gateway_ip, gateway_port, telegram_received_callback=None):
def __init__(self, xknx, src_address, local_ip, gateway_ip, gateway_port,
telegram_received_callback=None, auto_reconnect=False,
auto_reconnect_wait=3):
"""Initialize Tunnel class."""
# pylint: disable=too-many-arguments
self.xknx = xknx
Expand All @@ -38,6 +40,12 @@ def __init__(self, xknx, src_address, local_ip, gateway_ip, gateway_port, telegr
self.communication_channel = None
self.number_heartbeat_failed = 0

self.auto_reconnect = auto_reconnect
self.auto_reconnect_wait = auto_reconnect_wait

self._heartbeat_task = None
self._reconnect_task = None

def init_udp_client(self):
"""Initialize udp_client."""
self.udp_client = UDPClient(self.xknx,
Expand Down Expand Up @@ -85,11 +93,21 @@ async def connect(self):
self.udp_client)
await connect.start()
if not connect.success:
raise XKNXException("Could not establish connection")
if self.auto_reconnect:
msg = "Cannot connect to KNX. Retry in {} seconds.".format(
self.auto_reconnect_wait
)
self.xknx.logger.warning(msg)
task = self.xknx.loop.create_task(self.schedule_reconnect())
self._reconnect_task = task
return
else:
raise XKNXException("Could not establish connection")
self.xknx.logger.debug(
"Tunnel established communication_channel=%s, id=%s",
connect.communication_channel,
connect.identifier)
self._reconnect_task = None
self.communication_channel = connect.communication_channel
self.sequence_number = 0
await self.start_heartbeat()
Expand Down Expand Up @@ -150,6 +168,9 @@ async def connectionstate(self):

async def disconnect(self, ignore_error=False):
"""Disconnect from tunnel device."""
# only send disconnect request if we ever were connected
if self.communication_channel is None:
return
disconnect = Disconnect(
self.xknx,
self.udp_client,
Expand All @@ -166,15 +187,35 @@ async def reconnect(self):
self.init_udp_client()
await self.start()

async def schedule_reconnect(self):
"""Schedule reconnect to KNX."""
await asyncio.sleep(self.auto_reconnect_wait)
await self.reconnect()

async def stop_reconnect(self):
if self._reconnect_task is not None:
self._reconnect_task.cancel()
self._reconnect_task = None

async def stop(self):
"""Stop tunneling."""
await self.disconnect()
# XXX: set disconnect ignore_error True here. Is there actually anything
# which can happen if disconnect fails? normally this fails because
# we have no connection...
# await self.disconnect()
await self.disconnect(True)
await self.udp_client.stop()
await self.stop_heartbeat()
await self.stop_reconnect()

async def start_heartbeat(self):
"""Start heartbeat for monitoring state of tunnel, as suggested by 03.08.02 KNX Core 5.4."""
self.xknx.loop.create_task(
self.do_heartbeat())
self._heartbeat_task = self.xknx.loop.create_task(self.do_heartbeat())

async def stop_heartbeat(self):
if self._heartbeat_task is not None:
self._heartbeat_task.cancel()
self._heartbeat_task = None

async def do_heartbeat(self):
"""Heartbeat: Worker 'thread', endless loop for sending heartbeat requests."""
Expand All @@ -201,3 +242,4 @@ async def do_heartbeat_failed(self):
self.xknx.logger.warning("Heartbeat failed - reconnecting")
await self.reconnect()
self.number_heartbeat_failed = 0
await self.stop_heartbeat()

0 comments on commit 36f9a2d

Please sign in to comment.