Implement Counter and Date plugins in util.py.
authorBenjamin Braatz <bb@bbraatz.eu>
Mon, 25 Oct 2021 08:21:14 +0000 (10:21 +0200)
committerBenjamin Braatz <bb@bbraatz.eu>
Mon, 25 Oct 2021 08:21:14 +0000 (10:21 +0200)
controlpi_plugins/util.py

index 278fc3a2075e30ed76437ca1c2fba476cda84efa..d8b63a804cf7f4fdadf443fe4524104ae3a07d8d 100644 (file)
@@ -38,6 +38,7 @@ Test Log: {'sender': 'Test Alias', 'id': 'translated',
            'content': 'Test Message'}
 """
 import asyncio
+import datetime
 
 from controlpi import BasePlugin, Message, MessageTemplate
 
@@ -367,7 +368,7 @@ class Alias(BasePlugin):
 
     Optional configuration keys:
 
-    - 'to': translated message to be sent.
+    - 'to': translated message(s) to be sent.
     - 'translate': array of pairs of keys to be translated.
     """
 
@@ -407,3 +408,159 @@ class Alias(BasePlugin):
     async def run(self) -> None:
         """Run no code proactively."""
         pass
+
+
+class Counter(BasePlugin):
+    """Count messages confirming to a given template.
+
+    The plugin counts messages confirming to the given template. The
+    counter can be queried and reset by commands. The 'reset' command also
+    queries the last count before the reset:
+    >>> import controlpi
+    >>> asyncio.run(controlpi.test(
+    ...     {"Test Counter": {"plugin": "Counter",
+    ...                       "count": {"id": {"const": 42}}}},
+    ...     [{"target": "Test Counter", "command": "get count"},
+    ...      {"id": 42}, {"id": 42}, {"id": 49},
+    ...      {"target": "Test Counter", "command": "get count"},
+    ...      {"id": 42}, {"id": 42}, {"id": 42},
+    ...      {"target": "Test Counter", "command": "reset"},
+    ...      {"target": "Test Counter", "command": "get count"},
+    ...      {"id": 42}, {"id": 42}, {"id": 42},
+    ...      {"target": "Test Counter", "command": "get count"}]))
+    ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    test(): {'sender': '', 'event': 'registered',
+             'client': 'Test Counter', 'plugin': 'Counter',
+             'sends': [{'count': {'type': 'integer'}}],
+             'receives': [{'id': {'const': 42}},
+                          {'target': {'const': 'Test Counter'},
+                           'command': {'const': 'get count'}},
+                          {'target': {'const': 'Test Counter'},
+                           'command': {'const': 'reset'}}]}
+    test(): {'sender': 'test()', 'target': 'Test Counter',
+             'command': 'get count'}
+    test(): {'sender': 'Test Counter', 'count': 0}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'id': 49}
+    test(): {'sender': 'test()', 'target': 'Test Counter',
+             'command': 'get count'}
+    test(): {'sender': 'Test Counter', 'count': 2}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'target': 'Test Counter',
+             'command': 'reset'}
+    test(): {'sender': 'Test Counter', 'count': 5}
+    test(): {'sender': 'test()', 'target': 'Test Counter',
+             'command': 'get count'}
+    test(): {'sender': 'Test Counter', 'count': 0}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'id': 42}
+    test(): {'sender': 'test()', 'target': 'Test Counter',
+             'command': 'get count'}
+    test(): {'sender': 'Test Counter', 'count': 3}
+    """
+
+    CONF_SCHEMA = {'properties': {'count': {'type': 'object'}},
+                   'required': ['count']}
+    """Schema for Counter plugin configuration.
+
+    Required configuration key:
+
+    - 'count': template of messages to be counted.
+    """
+
+    def process_conf(self) -> None:
+        """Register plugin as bus client."""
+        self._count = 0
+        self._template = MessageTemplate(self.conf['count'])
+        sends = [MessageTemplate({'count': {'type': 'integer'}})]
+        receives = [self._template,
+                    MessageTemplate({'target': {'const': self.name},
+                                     'command': {'const': 'get count'}}),
+                    MessageTemplate({'target': {'const': self.name},
+                                     'command': {'const': 'reset'}})]
+        self.bus.register(self.name, 'Counter', sends, receives, self.receive)
+
+    async def receive(self, message: Message) -> None:
+        """Send current date and time as message."""
+        if self._template.check(message):
+            self._count += 1
+        if ('target' in message and message['target'] == self.name and
+                'command' in message):
+            if message['command'] == 'get count':
+                await self.bus.send(Message(self.name, {'count': self._count}))
+            elif message['command'] == 'reset':
+                count = self._count
+                self._count = 0
+                await self.bus.send(Message(self.name, {'count': count}))
+
+    async def run(self) -> None:
+        """Run no code proactively."""
+        pass
+
+
+class Date(BasePlugin):
+    """Send message with current date.
+
+    The plugin reacts to 'get date' commands by sending messages with
+    a 'date' key:
+    >>> import controlpi
+    >>> asyncio.run(controlpi.test(
+    ...     {"Test Date": {"plugin": "Date"}},
+    ...     [{"target": "Test Date", "command": "get date"}]))
+    ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    test(): {'sender': '', 'event': 'registered',
+             'client': 'Test Date', 'plugin': 'Date',
+             'sends': [{'date': {'type': 'string'}}],
+             'receives': [{'target': {'const': 'Test Date'},
+                           'command': {'const': 'get date'}}]}
+    test(): {'sender': 'test()', 'target': 'Test Date',
+             'command': 'get date'}
+    test(): {'sender': 'Test Date', 'date': ...}
+
+    The format of the date can be configured with the 'format'
+    configuration key:
+    >>> asyncio.run(controlpi.test(
+    ...     {"Test Date": {"plugin": "Date",
+    ...                    "format": "%Y%m%d%H%M%S%f"}},
+    ...     [{"target": "Test Date", "command": "get date"}]))
+    ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    test(): {'sender': '', 'event': 'registered',
+             'client': 'Test Date', 'plugin': 'Date',
+             'sends': [{'date': {'type': 'string'}}],
+             'receives': [{'target': {'const': 'Test Date'},
+                           'command': {'const': 'get date'}}]}
+    test(): {'sender': 'test()', 'target': 'Test Date',
+             'command': 'get date'}
+    test(): {'sender': 'Test Date', 'date': ...}
+    """
+
+    CONF_SCHEMA = {'properties': {'format':
+                                  {'type': 'string',
+                                   'default': '%Y-%m-%d %H:%M:%S'}}}
+    """Schema for Date plugin configuration.
+
+    Optional configuration key:
+
+    - 'format': format for the sent datetime string.
+                Default: '%Y-%m-%d %H:%M:%S'
+    """
+
+    def process_conf(self) -> None:
+        """Register plugin as bus client."""
+        sends = [MessageTemplate({'date': {'type': 'string'}})]
+        receives = [MessageTemplate({'target': {'const': self.name},
+                                     'command': {'const': 'get date'}})]
+        self.bus.register(self.name, 'Date', sends, receives, self.date)
+
+    async def date(self, message: Message) -> None:
+        """Send current date and time as message."""
+        date = datetime.datetime.now().strftime(self.conf['format'])
+        await self.bus.send(Message(self.name, {'date': date}))
+
+    async def run(self) -> None:
+        """Run no code proactively."""
+        pass