TODO: documentation, doctests
"""
import asyncio
+import fcntl
import json
+import socket
+import struct
from websockets import ConnectionClosed, connect
from controlpi import BasePlugin, Message, MessageTemplate
CONF_SCHEMA = {'properties':
{'url': {'type': 'string'},
+ 'interface': {'type': 'string'},
'client': {'type': 'string'},
'up filter': {'type': 'array',
'items': {'type': 'object'}},
'down filter': {'type': 'array',
'items': {'type': 'object'}}},
- 'required': ['url', 'client', 'up filter', 'down filter']}
+ 'required': ['url', 'up filter', 'down filter']}
async def _receive(self, message: Message) -> None:
if not self._websocket:
return
assert isinstance(message['sender'], str)
prefix = f"{self.name}/"
- original_sender = self.conf['client']
+ original_sender = self._client
if 'original sender' in message:
if message['original sender'].startswith(prefix):
return
original_sender += f"/{message['original sender']}"
- else:
+ elif message['sender'] != '':
original_sender += f"/{message['sender']}"
message['original sender'] = original_sender
del message['sender']
if 'target' in message:
assert isinstance(message['target'], str)
target = message['target']
- if target.startswith(prefix):
+ if target == '':
+ target = self._client
+ elif target.startswith(prefix):
target = target[len(prefix):]
else:
- target = f"{self.conf['client']}/{target}"
+ target = f"{self._client}/{target}"
message['target'] = target
json_message = json.dumps(message)
await self._websocket.send(json_message)
async def _send(self, json_message: str) -> None:
message = json.loads(json_message)
- prefix = f"{self.conf['client']}/"
+ prefix = f"{self._client}/"
original_sender = self.name
if 'original sender' in message:
if message['original sender'].startswith(prefix):
return
original_sender += f"/{message['original sender']}"
- else:
+ elif message['sender'] != '':
original_sender += f"/{message['sender']}"
message['original sender'] = original_sender
message['sender'] = self.name
if 'target' in message:
target = message['target']
- if target.startswith(prefix):
+ if target == '':
+ target = self.name
+ elif target.startswith(prefix):
target = target[len(prefix):]
else:
target = f"{self.name}/{target}"
def process_conf(self) -> None:
"""Register plugin as bus client."""
self._websocket = None
+ if 'client' in self.conf:
+ self._client = self.conf['client']
+ if 'interface' in self.conf:
+ # Get own MAC address:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ info = fcntl.ioctl(sock.fileno(), 0x8927,
+ struct.pack('256s',
+ bytes(self.conf['interface'],
+ 'utf-8')[:15]))
+ self._mac = ':'.join('%02x' % b for b in info[18:24])
sends = []
sends.append(MessageTemplate({'event':
{'const': 'registered'}}))
'const' in send_template['original sender']):
const = send_template['original sender']['const']
original_sender += f"/{const}"
- else:
+ elif send_template['sender']['const'] != '':
const = send_template['sender']['const']
original_sender += f"/{const}"
send_template['original sender'] = {'const': original_sender}
if ('target' in send_template and
'const' in send_template['target']):
target = send_template['target']['const']
- prefix = f"{self.conf['client']}/"
- if target.startswith(prefix):
- target = target[len(prefix):]
+ if target == '':
+ target = self.name
+ elif 'client' in self.conf:
+ prefix = f"{self.conf['client']}/"
+ if target.startswith(prefix):
+ target = target[len(prefix):]
+ else:
+ target = f"{self.name}/{target}"
else:
target = f"{self.name}/{target}"
send_template['target'] = {'const': target}
async def run(self) -> None:
"""Connect to wsserver and process messages from it."""
- up_filter = []
- for template in self.conf['up filter']:
- up_template = MessageTemplate(template)
- if ('sender' in up_template and
- 'const' in up_template['sender']):
- original_sender = self.conf['client']
- if ('original sender' in up_template and
- 'const' in up_template['original sender']):
- const = up_template['original sender']['const']
- original_sender += f"/{const}"
- else:
- const = up_template['sender']['const']
- original_sender += f"/{const}"
- up_template['original sender'] = {'const': original_sender}
- del up_template['sender']
- if ('target' in up_template and
- 'const' in up_template['target']):
- target = up_template['target']['const']
- prefix = f"{self.name}/"
- if target.startswith(prefix):
- target = target[len(prefix):]
- else:
- target = f"{self.conf['client']}/{target}"
- up_template['target'] = {'const': target}
- up_filter.append(up_template)
- conf_command = {'command': 'configure websocket',
- 'target': '',
- 'name': self.conf['client'],
- 'up filter': up_filter,
- 'down filter': self.conf['down filter']}
- json_command = json.dumps(conf_command)
while True:
try:
async with connect(self.conf['url']) as websocket:
+ conf_command = {'command': 'configure websocket',
+ 'target': ''}
+ if 'client' in self.conf:
+ conf_command['name'] = self._client
+ else:
+ address = websocket.local_address[0]
+ port = websocket.local_address[1]
+ self._client = f"{address}:{port}"
+ if 'interface' in self.conf:
+ conf_command['mac'] = self._mac
+ up_filter = []
+ for template in self.conf['up filter']:
+ up_template = MessageTemplate(template)
+ if ('sender' in up_template and
+ 'const' in up_template['sender']):
+ original_sender = self._client
+ if ('original sender' in up_template and
+ 'const' in up_template['original sender']):
+ const = up_template['original sender']['const']
+ original_sender += f"/{const}"
+ elif up_template['sender']['const'] != '':
+ const = up_template['sender']['const']
+ original_sender += f"/{const}"
+ up_template['original sender'] = {'const': original_sender}
+ del up_template['sender']
+ if ('target' in up_template and
+ 'const' in up_template['target']):
+ target = up_template['target']['const']
+ prefix = f"{self.name}/"
+ if target == '':
+ target = self._client
+ elif target.startswith(prefix):
+ target = target[len(prefix):]
+ else:
+ target = f"{self._client}/{target}"
+ up_template['target'] = {'const': target}
+ up_filter.append(up_template)
+ conf_command['up filter'] = up_filter
+ conf_command['down filter'] = self.conf['down filter']
+ json_command = json.dumps(conf_command)
await websocket.send(json_command)
await self.bus.send(Message(self.name,
{'event':