From: Benjamin Braatz Date: Wed, 17 Mar 2021 14:39:15 +0000 (+0100) Subject: Split util.py and start documentation X-Git-Tag: v0.3.0~57 X-Git-Url: http://git.graph-it.com/?a=commitdiff_plain;h=a8400ff04549cafdb5a673ff82633941547de324;p=graphit%2Fcontrolpi.git Split util.py and start documentation --- diff --git a/controlpi-plugins/state.py b/controlpi-plugins/state.py new file mode 100644 index 0000000..b6c762f --- /dev/null +++ b/controlpi-plugins/state.py @@ -0,0 +1,42 @@ +"""Provide state plugins for all kinds of systems. + +TODO: documentation, doctests +TODO: AndState, OrState +""" +from controlpi import BasePlugin, Message + + +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) + + async def run(self) -> None: + pass diff --git a/controlpi-plugins/util.py b/controlpi-plugins/util.py index 29d892e..2200c55 100644 --- a/controlpi-plugins/util.py +++ b/controlpi-plugins/util.py @@ -1,93 +1,135 @@ """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: @@ -96,46 +138,12 @@ class Alias(BasePlugin): 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 diff --git a/controlpi-plugins/wait.py b/controlpi-plugins/wait.py new file mode 100644 index 0000000..afe5431 --- /dev/null +++ b/controlpi-plugins/wait.py @@ -0,0 +1,44 @@ +"""Provide waiting/sleeping plugins for all kinds of systems. + +TODO: documentation, doctests +""" +import asyncio + +from controlpi import BasePlugin, Message + + +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 + + +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