"""Provide utility plugins for all kinds of systems.
-TODO: distribute over several modules
+- Log logs messages on stdout.
+- Init sends list of messages on startup and on demand.
+- Alias translates messages to an alias.
+
+>>> import controlpi
+>>> import asyncio
+>>> async def test():
+... run_task = asyncio.create_task(controlpi.run(
+... {"Debug Log": {"plugin": "Log",
+... "filter": [{}]},
+... "Test Init": {"plugin": "Init",
+... "messages": [{"id": 42,
+... "content": "Test Message"}]},
+... "Test Alias": {"plugin": "Alias",
+... "from": {"sender": {"const": "Test Init"},
+... "id": {"const": 42}},
+... "to": {"id": "translated"}}}
+... ))
+... for i in range(7):
+... await asyncio.sleep(0)
+... run_task.cancel()
+... await run_task
+>>> asyncio.run(test()) # doctest: +NORMALIZE_WHITESPACE
+Debug Log: {'sender': '', 'event': 'registered',
+ 'client': 'Debug Log', 'plugin': 'Log',
+ 'sends': [], 'receives': [{}]}
+Debug Log: {'sender': '', 'event': 'registered',
+ 'client': 'Test Init', 'plugin': 'Init',
+ 'sends': [{'id': {'const': 42},
+ 'content': {'const': 'Test Message'}},
+ {'target': {'const': 'Test Init'},
+ 'command': {'const': 'execute'}}],
+ 'receives': [{'target': {'const': 'Test Init'},
+ 'command': {'const': 'execute'}}]}
+Debug Log: {'sender': '', 'event': 'registered',
+ 'client': 'Test Alias', 'plugin': 'Alias',
+ 'sends': [{'id': {'const': 'translated'}}],
+ 'receives': [{'sender': {'const': 'Test Init'},
+ 'id': {'const': 42}}]}
+Debug Log: {'sender': 'Test Init',
+ 'target': 'Test Init', 'command': 'execute'}
+Debug Log: {'sender': 'Test Init',
+ 'id': 42, 'content': 'Test Message'}
+Debug Log: {'sender': 'Test Alias',
+ 'id': 'translated', 'content': 'Test Message'}
+
TODO: documentation, doctests
-TODO: AndState, OrState?
"""
-import asyncio
-
from controlpi import BasePlugin, Message, MessageTemplate
class Log(BasePlugin):
+ """Log messages on stdout.
+
+ """
+
CONF_SCHEMA = {'properties': {'filter': {'type': 'array'}},
'required': ['filter']}
+ """Schema for Log plugin configuration.
+
+ Required configuration key:
+
+ - 'filter' with list of message templates to be logged.
+ """
async def log(self, message: Message) -> None:
+ """Log received message on stdout using own name as prefix."""
print(f"{self.name}: {message}")
def process_conf(self) -> None:
+ """Register plugin as bus client."""
self.bus.register(self.name, 'Log', [], self.conf['filter'], self.log)
async def run(self) -> None:
+ """Run no code proactively."""
pass
class Init(BasePlugin):
+ """Send list of messages on startup and on demand.
+
+ """
+
CONF_SCHEMA = {'properties': {'messages': {'type': 'array'}},
'required': ['messages']}
+ """Schema for Init plugin configuration.
+
+ Required configuration key:
+
+ - 'messages' with list of messages to be sent.
+ """
async def execute(self, message: Message) -> None:
+ """Send configured messages."""
for message in self.conf['messages']:
await self.bus.send(Message(self.name, message))
def process_conf(self) -> None:
- receives = [{'target': {'const': self.name},
- 'command': {'const': 'execute'}}]
+ """Register plugin as bus client."""
+ receives = [MessageTemplate({'target': {'const': self.name},
+ 'command': {'const': 'execute'}})]
sends = [MessageTemplate.from_message(message)
for message in self.conf['messages']]
sends.extend(receives)
self.bus.register(self.name, 'Init', sends, receives, self.execute)
async def run(self) -> None:
- await self.bus.send({'sender': self.name,
- 'target': self.name,
- 'command': 'execute'})
-
-
-class Wait(BasePlugin):
- CONF_SCHEMA = {'properties': {'seconds': {'type': 'number'}},
- 'required': ['seconds']}
-
- async def wait(self, message: Message) -> None:
- await asyncio.sleep(self.conf['seconds'])
- await self.bus.send({'sender': self.name, 'event': 'finished'})
-
- def process_conf(self) -> None:
- receives = [{'target': {'const': self.name},
- 'command': {'const': 'wait'}}]
- sends = [{'event': {'const': 'finished'}}]
- self.bus.register(self.name, 'Wait', sends, receives, self.wait)
-
- async def run(self) -> None:
- pass
+ """Send execution command on startup."""
+ await self.bus.send(Message(self.name, {'target': self.name,
+ 'command': 'execute'}))
-class GenericWait(BasePlugin):
- CONF_SCHEMA = True
-
- async def wait(self, message: Message) -> None:
- await asyncio.sleep(message['seconds'])
- await self.bus.send(Message(self.name, {'id': message['id']}))
-
- def process_conf(self) -> None:
- receives = [{'target': {'const': self.name},
- 'command': {'const': 'wait'},
- 'seconds': {'type': 'number'},
- 'id': {'type': 'string'}}]
- sends = [{'id': {'type': 'string'}}]
- self.bus.register(self.name, 'GenericWait', sends, receives, self.wait)
-
- async def run(self) -> None:
- pass
+class Alias(BasePlugin):
+ """Translate messages to an alias.
+ """
-class Alias(BasePlugin):
CONF_SCHEMA = {'properties': {'from': {'type': 'object'},
'to': {'type': 'object'}},
'required': ['from', 'to']}
+ """Schema for Alias plugin configuration.
+
+ Required configuration keys:
+
+ - 'from' with template of messages to be translated.
+ - 'to' with translated message to be sent.
+ """
async def alias(self, message: Message) -> None:
+ """Translate and send message."""
alias_message = Message(self.name)
alias_message.update(self.conf['to'])
for key in message:
await self.bus.send(alias_message)
def process_conf(self) -> None:
+ """Register plugin as bus client."""
self.bus.register(self.name, 'Alias',
- [MessageTemplate.from_message(self.conf['to'])],
- [self.conf['from']],
- self.alias)
-
- async def run(self) -> None:
- pass
-
-
-class State(BasePlugin):
- CONF_SCHEMA = True
-
- async def receive(self, message: Message) -> None:
- if message['command'] == 'get state':
- answer = {'sender': self.name, 'state': self.state}
- await self.bus.send(answer)
- elif message['command'] == 'set state':
- if self.state != message['new state']:
- self.state: bool = message['new state']
- event = {'sender': self.name, 'event': 'changed',
- 'state': self.state}
- await self.bus.send(event)
- else:
- answer = {'sender': self.name, 'state': self.state}
- await self.bus.send(answer)
-
- def process_conf(self) -> None:
- self.state = False
- sends = [{'event': {'const': 'changed'},
- 'state': {'type': 'boolean'}},
- {'state': {'type': 'boolean'}}]
- receives = [{'target': {'const': self.name},
- 'command': {'const': 'get state'}},
- {'target': {'const': self.name},
- 'command': {'const': 'set state'},
- 'new state': {'type': 'boolean'}}]
- self.bus.register(self.name, 'State',
- sends,
- receives,
- self.receive)
+ [MessageTemplate.from_message(self.conf['to'])],
+ [self.conf['from']],
+ self.alias)
async def run(self) -> None:
+ """Run no code proactively."""
pass