--- /dev/null
+import aiofiles
+import asyncio
+import glob
+
+from typing import Dict, List
+
+from controlpi import BasePlugin, Message, MessageTemplate
+
+
+class UniPi(BasePlugin):
+ CONF_SCHEMA = {}
+
+ def process_conf(self) -> None:
+ """Search all input and output pins and initialise."""
+ self._paths: Dict[str, str] = {}
+ self._states: Dict[str, bool] = {}
+ self._counters: Dict[str, int] = {}
+ for path in glob.glob('/sys/bus/platform/devices/unipi_plc/io_group?/di_?_??'):
+ client = path[46:53]
+ self._paths[client] = path
+ with open(f"{path}/{client[0:2]}_value", 'r') as sysfs_file:
+ file_state = sysfs_file.read()
+ state = False
+ if file_state[0] == '1':
+ state = True
+ self._states[client] = state
+ with open(f"{path}/counter", 'r') as sysfs_file:
+ file_counter = sysfs_file.read()
+ self._counters[client] = int(file_counter)
+ self.bus.register(client, 'UniPiInput',
+ [MessageTemplate({'event':
+ {'const': 'changed'},
+ 'state':
+ {'type': 'boolean'}}),
+ MessageTemplate({'state':
+ {'type': 'boolean'}}),
+ MessageTemplate({'event':
+ {'const': 'changed'},
+ 'counter':
+ {'type': 'integer'}}),
+ MessageTemplate({'counter':
+ {'type': 'integer'}})],
+ [([MessageTemplate({'target':
+ {'const': client},
+ 'command':
+ {'const': 'get state'}})],
+ self._get_state),
+ ([MessageTemplate({'target':
+ {'const': client},
+ 'command':
+ {'const': 'get counter'}})],
+ self._get_counter),
+ ([MessageTemplate({'target':
+ {'const': client},
+ 'command':
+ {'const': 'reset counter'}})],
+ self._reset_counter)])
+ for path in glob.glob('/sys/bus/platform/devices/unipi_plc/io_group?/do_?_??'):
+ client = path[46:53]
+ self._paths[client] = path
+ with open(f"{path}/{client[0:2]}_value", 'r') as sysfs_file:
+ file_state = sysfs_file.read()
+ state = False
+ if file_state[0] == '1':
+ state = True
+ self._states[client] = state
+ self.bus.register(client, 'UniPiOutput',
+ [MessageTemplate({'event':
+ {'const': 'changed'},
+ 'state':
+ {'type': 'boolean'}}),
+ MessageTemplate({'state':
+ {'type': 'boolean'}})],
+ [([MessageTemplate({'target':
+ {'const': client},
+ 'command':
+ {'const': 'get state'}})],
+ self._get_state),
+ ([MessageTemplate({'target':
+ {'const': client},
+ 'command':
+ {'const': 'set state'},
+ 'new state':
+ {'type': 'boolean'}})],
+ self._set_state)])
+
+ async def _get_state(self, message: Message) -> None:
+ assert isinstance(message['target'], str)
+ client = message['target']
+ path = self._paths[client]
+ # Get state from sysfs file:
+ old_state = self._states[client]
+ new_state = False
+ async with aiofiles.open(f"{path}/{client[0:2]}_value", 'r') as sysfs_file:
+ file_state = await sysfs_file.read()
+ if file_state[0] == '1':
+ new_state = True
+ self._states[client] = new_state
+ if old_state == new_state:
+ await self.bus.send(Message(client, {'state': new_state}))
+ else:
+ await self.bus.send(Message(client, {'event': 'changed',
+ 'state': new_state}))
+
+ async def _get_counter(self, message: Message) -> None:
+ assert isinstance(message['target'], str)
+ client = message['target']
+ path = self._paths[client]
+ # Get counter from sysfs file:
+ old_counter = self._counters[client]
+ new_counter = 0
+ async with aiofiles.open(f"{path}/counter", 'r') as sysfs_file:
+ file_counter = await sysfs_file.read()
+ new_counter = int(file_counter)
+ self._counters[client] = new_counter
+ if old_counter == new_counter:
+ await self.bus.send(Message(client, {'counter': new_counter}))
+ else:
+ await self.bus.send(Message(client, {'event': 'changed',
+ 'counter': new_counter}))
+
+ async def _reset_counter(self, message: Message) -> None:
+ assert isinstance(message['target'], str)
+ client = message['target']
+ path = self._paths[client]
+ # Write zero to sysfs file:
+ async with aiofiles.open(f"{path}/counter", 'w') as sysfs_file:
+ await sysfs_file.write('0')
+ # Get counter from sysfs file:
+ old_counter = self._counters[client]
+ new_counter = 0
+ async with aiofiles.open(f"{path}/counter", 'r') as sysfs_file:
+ file_counter = await sysfs_file.read()
+ new_counter = int(file_counter)
+ self._counters[client] = new_counter
+ if old_counter == new_counter:
+ await self.bus.send(Message(client, {'counter': new_counter}))
+ else:
+ await self.bus.send(Message(client, {'event': 'changed',
+ 'counter': new_counter}))
+
+ async def _set_state(self, message: Message) -> None:
+ assert isinstance(message['target'], str)
+ client = message['target']
+ path = self._paths[client]
+ # Write new state to sysfs file:
+ async with aiofiles.open(f"{path}/{client[0:2]}_value", 'w') as sysfs_file:
+ if message['new state']:
+ await sysfs_file.write('1')
+ else:
+ await sysfs_file.write('0')
+ # Get state from sysfs file:
+ old_state = self._states[client]
+ new_state = False
+ async with aiofiles.open(f"{path}/{client[0:2]}_value", 'r') as sysfs_file:
+ file_state = await sysfs_file.read()
+ if file_state[0] == '1':
+ new_state = True
+ self._states[client] = new_state
+ if old_state == new_state:
+ await self.bus.send(Message(client, {'state': new_state}))
+ else:
+ await self.bus.send(Message(client, {'event': 'changed',
+ 'state': new_state}))
+
+ async def run(self) -> None:
+ """Poll states."""
+ while True:
+ await asyncio.sleep(0.05)
+ for client in self._states:
+ path = self._paths[client]
+ # Get state from sysfs file:
+ old_state = self._states[client]
+ new_state = False
+ async with aiofiles.open(f"{path}/{client[0:2]}_value", 'r') as sysfs_file:
+ file_state = await sysfs_file.read()
+ if file_state[0] == '1':
+ new_state = True
+ self._states[client] = new_state
+ if old_state != new_state:
+ await self.bus.send(Message(client, {'event': 'changed',
+ 'state': new_state}))
+ for client in self._counters:
+ path = self._paths[client]
+ # Get counter from sysfs file:
+ old_counter = self._counters[client]
+ new_counter = 0
+ async with aiofiles.open(f"{path}/counter", 'r') as sysfs_file:
+ file_counter = await sysfs_file.read()
+ new_counter = int(file_counter)
+ self._counters[client] = new_counter
+ if old_counter != new_counter:
+ await self.bus.send(Message(client, {'event': 'changed',
+ 'counter': new_counter}))