class Driver(threading.Thread): ''' driver class not used directly to standardize any type of protocol ''' _logger = get_logger('rhum.drivers.Driver') def __init__(self, callback=None): super(Driver, self).__init__() # Create an event to stop the thread self._stop = threading.Event() # Setup messages queues self.transmit = queue.Queue() self.receive = queue.Queue() # Set the callback method self.__callback = callback def send(self, message): if not isinstance(message, EnOceanMessage): self._logger.error('Cannot send others objects than Message') return False self.transmit.put(message) return True def stop(self): self._stop.set() def test(self): return True def parse(self): ''' Parses messages and puts them to receive queue '''
class RadioERP1Message(EnOceanMessage): _logger = get_logger('rhum.drivers.enocean.messages.RadioERP1Message') def __init__(self, __type, __datas, __opts): self.__rorg = __datas[0:1][0] self.__subtelnum = None self.__destID = None self.__dBm = None self.__securityLevel = None if len(__opts) > 0: self.__subtelnum = __opts[0:1][0] self.__destID = ''.join(["%02X " % x for x in __opts[1:5]]).strip() self.__dBm = ''.join(["%02X " % x for x in __opts[5:6]]).strip() self.__securityLevel = ''.join(["%02X " % x for x in __opts[6:]]).strip() super(RadioERP1Message, self).__init__(__type, __datas, __opts) def isRadioERP1(self): return (self._get()[0] == PacketType.RADIO_ERP1.value) def getReturnType(self): return RadioERP1Type(self.__rorg) def __str__(self): strMsg = super(RadioERP1Message, self).__str__() strMsg += "\nR-ORG : {0}".format( RadioERP1Type(self.__rorg)) strMsg += "\nSubTelNum : {0}".format(self.__subtelnum) strMsg += "\nDestination ID : {0}".format(self.__destID) strMsg += "\ndBm : {0}".format(self.__dBm) strMsg += "\nSecurity Level : {0}".format(self.__securityLevel) return strMsg
class VersionMessage(ResponseMessage): _logger = get_logger('rhum.drivers.enocean.messages.VersionMessage') def __init__(self, __type, __datas, __opts): super(VersionMessage, self).__init__(__type, __datas, __opts) self.__appmain = __datas[1:2][0] self.__appbeta = __datas[2:3][0] self.__appalpha = __datas[3:4][0] self.__appbuild = __datas[4:5][0] self.__apimain = __datas[5:6][0] self.__apibeta = __datas[6:7][0] self.__apialpha = __datas[7:8][0] self.__apibuild = __datas[8:9][0] self.__chipid = ''.join(["%02X " % x for x in __datas[9:13]]).strip() self.__chipversion = ''.join(["%02X " % x for x in __datas[13:17]]).strip() self.__desc = __datas[17:-1].decode(encoding='ASCII').strip() def __str__(self): strMsg = super(VersionMessage, self).__str__() strMsg += "\nApplication Version : {0}.{1}.{2}.{3}".format( self.__appmain, self.__appbeta, self.__appalpha, self.__appbuild) strMsg += "\nAPI Version : {0}.{1}.{2}.{3}".format( self.__apimain, self.__apibeta, self.__apialpha, self.__apibuild) strMsg += "\nChip ID : {0}".format(self.__chipid) strMsg += "\nChip Version : {0}".format(self.__chipversion) strMsg += "\nDescription : {0}".format(self.__desc) return strMsg
class EnOceanMessage: _logger = get_logger('rhum.drivers.enocean.EnOceanMessage') __syncByte = 0x55 def __init__(self, msgType=0xFF, datas=None, optDatas=None): self.__type = msgType self.__datas = [] self.__optDatas = [] if datas != None: self.__datas = datas if optDatas != None: self.__optDatas = optDatas def build(self): self._logger.debug('building message') buffer = [] data_length = len(self.__datas) opt_length = len(self.__optDatas) #sync byte buffer.append(self.__syncByte) # adding sync byte #header buffer.append((data_length >> 8) & 0xFF) #first byte length data buffer.append(data_length & 0xFF) #second byte length data buffer.append(opt_length & 0xFF) #optionnal data length buffer.append(self.__type & 0xFF) #packet type byte #CRC Header buffer.append(CRC8Utils.calc(buffer[1:5])) #data buffer += self.__datas buffer += self.__optDatas #CRC Data buffer.append(CRC8Utils.calc(buffer[6:])) self._logger.debug('buffer : {0}'.format(buffer)) return buffer def _get(self): return self.__type, self.__datas, self.__optDatas def __str__(self): msg = ''.join(["\\x%02X" % x for x in bytes(self.build())]).strip() strMsg = "\nMessage : {0}".format(msg) strMsg += "\nType : {0}".format(PacketType( self.__type)) strMsg += "\nData Length : {0}".format(len(self.__datas)) strMsg += "\nOpt Length : {0}".format(len(self.__optDatas)) return strMsg
class Item: '''Item representation of all type of items''' __logger = get_logger('rhum.messages.item.Item') __type = None __value = None __id = None def __init__(self, itemType, itemId, itemValue=None): self.__type = itemType self.__id = itemId self.__value = itemValue
class RPSMessage(RadioERP1Message): _logger = get_logger('rhum.drivers.enocean.messages.radioerp1.RPSMessage') def __init__(self, __type, __datas, __opts): super(RPSMessage, self).__init__(__type, __datas, __opts) self.__data = ''.join(["%02X " % x for x in __datas[1:2]]).strip() self.__senderID = ''.join(["%02X " % x for x in __datas[2:6]]).strip() self.__status = ''.join(["%02X " % x for x in __datas[6:]]).strip() def __str__(self): strMsg = super(RPSMessage, self).__str__() strMsg += "\nSender ID : {0}".format(self.__senderID) strMsg += "\nStatus : {0}".format(self.__status) strMsg += "\nData : {0}".format(self.__data) return strMsg
class ResponseMessage(EnOceanMessage): _logger = get_logger('rhum.drivers.enocean.messages.ResponseMessage') def __init__(self, __type, __datas, __opts): super(ResponseMessage, self).__init__(__type, __datas, __opts) self.__returnCode = __datas[0:1][0] def isResponse(self): return (self._get()[0] == PacketType.RESPONSE.value) def getReturnCode(self): return ResponseType(self.__returnCode) def __str__(self): strMsg = super(ResponseMessage, self).__str__() strMsg += "\nResponse Return Code : {0}".format( ResponseType(self.__returnCode)) return strMsg
class TypingMessage: _logger = get_logger('rhum.drivers.enocean.messages.TypingMessage') @classmethod def transform(cls, _type, _datas, _opts): msg = None if (PacketType(_type) == PacketType.RESPONSE): cls._logger.debug('receiving Response') msg = ResponseMessage(_type, _datas, _opts) elif (PacketType(_type) == PacketType.RADIO_ERP1): cls._logger.debug('receiving Radio ERP 1') msg = cls.__transformRadioERP1(_type, _datas, _opts) else: cls._logger.debug('receiving Unknow {0}'.format(PacketType(_type))) msg = EnOceanMessage(_type, _datas, _opts) return msg @classmethod def __transformRadioERP1(cls, _type, _datas, _opts): msg = RadioERP1Message(_type, _datas, _opts) if (RadioERP1Type.RPS == msg.getReturnType()): cls._logger.debug('receiving Radio ERP 1 : RPS') msg = RPSMessage(_type, _datas, _opts) elif (RadioERP1Type.BS1 == msg.getReturnType()): cls._logger.debug('receiving Radio ERP 1 : BS1') msg = BS1Message(_type, _datas, _opts) elif (RadioERP1Type.BS4 == msg.getReturnType()): cls._logger.debug('receiving Radio ERP 1 : BS4') msg = BS4Message(_type, _datas, _opts) else: cls._logger.debug('receiving Radio ERP 1 : {0}'.format( RadioERP1Type(_datas[0:1][0]))) return msg
class BS4Message(RadioERP1Message): _logger = get_logger('rhum.drivers.enocean.messages.radioerp1.BS1Message') def __init__(self, __type, __datas, __opts): super(BS4Message, self).__init__(__type, __datas, __opts) self.__senderID = ''.join(["%02X " % x for x in __datas[5:9]]).strip() self.__status = ''.join(["%02X " % x for x in __datas[9:]]).strip() self.__data3 = ''.join(["%02X " % x for x in __datas[1:2]]).strip() self.__data2 = ''.join(["%02X " % x for x in __datas[2:3]]).strip() self.__data1 = ''.join(["%02X " % x for x in __datas[3:4]]).strip() self.__data0 = ''.join(["%02X " % x for x in __datas[4:5]]).strip() def __str__(self): strMsg = super(BS4Message, self).__str__() strMsg += "\nSender ID : {0}".format(self.__senderID) strMsg += "\nStatus : {0}".format(self.__status) strMsg += "\nData 0 : {0}".format(self.__data0) strMsg += "\nData 1 : {0}".format(self.__data1) strMsg += "\nData 2 : {0}".format(self.__data2) strMsg += "\nData 3 : {0}".format(self.__data3) return strMsg
class RhumDaemon(Daemon): __logger = get_logger('rhum.RhumDaemon') __driver_manager = DriverManager() def run(self): self.__load() while True: time.sleep(1) '''stop the daemon service''' def stop(self): self.__unload() return Daemon.stop(self) '''reload service method for deamon''' def reload(self): self.__unload() self.__load() '''Load driver manager to search and initiate drivers''' def __load(self): self.__logger.info('Loading service managers') self.__driver_manager.start() return True '''unloading threads correctly corresponding to drivers''' def __unload(self): self.__logger.info('Unloading service managers') self.__driver_manager.stop() if self.__driver_manager.isAlive(): self.__driver_manager.join() return True
import sys from rhum.rhumlogging import get_logger from rhum.rhum import RhumDaemon _pid_file = '/tmp/daemon-rhum.pid' if __name__ == "__main__": logger = get_logger('RHUM-MAIN') logger.debug('rhum service manager : ') daemon = RhumDaemon(_pid_file) if len(sys.argv) == 2: if 'start' == sys.argv[1]: logger.info("Starting Rhum") daemon.start() elif 'stop' == sys.argv[1]: logger.info("Stopping Rhum") daemon.stop() elif 'restart' == sys.argv[1]: logger.info("Restarting Rhum") daemon.restart() elif 'reload' == sys.argv[1]: logger.info("Reloading Rhum") daemon.reload() else: logger.error("Unknow command of Rhum service") sys.exit(2) sys.exit(0) else: print("usage: {0} start|stop|restart".format(sys.argv[0]))
class DriverManager(Thread): ''' Driver Manager Class used to initiate the drivers ''' _logger = get_logger('rhum.drivers.DriverManager') def __init__(self): super(DriverManager, self).__init__() # Create an event to stop the thread self.__stop = Event() self.__stop.clear() self.__drivers = [] def stop(self): self.__stop.set() self._logger.info('stopping drivers') for driver in self.__drivers: driver.stop() #self.join() #drv.join() ''' driver Manager run ''' def run(self): self._logger.info('initialize driver manager') #init the drivers searching self.__search_driver() for drv in self.__drivers: self._logger.info('starting driver {0}'.format(drv)) drv.start() #while runing pause the manager in waiting command while not self.__stop.is_set(): time.sleep(1) '''search and initiate driver manager''' def __search_driver(self): path = os.listdir(path='/dev/') tty = [] for file in path: if file.find('AMA') != -1: tty.append(file) elif file.find('USB') != -1: tty.append(file) self._logger.debug('List tty') self._logger.debug(len(tty)) '''test each tty port to connect with each driver''' for port_path in tty: path = '/dev/{0}'.format(port_path) self._logger.debug('test EnOcean Driver for {0}'.format(path)) drive = EnOceanDriver(path) if drive.test(): self._logger.info( 'EnOcean Driver selected for {0}'.format(path)) self.__drivers.append(drive) continue self._logger.info('No Driver selected for {0}'.format(path)) return True
class _CRC8: _logger = get_logger('rhum.utils.crc8') # CRC8 encode table will keep the calculated CRC8 of each byte to prevent multi calculation _crc8Encode = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ] # ##### # Internal method used to calculate the CRC8 for one byte # and keep it in the corresponding entry of "crc8Encode" # ex: calculCRC8(0) will keep 0x00 in crc8Encode[0] def _calculCRC8(self,byte): self._logger.debug('no value in table for {0}'.format(byte)) crc = byte; for j in range(0,8): self._logger.debug('run {0}'.format(j)) crc = (crc << 1) ^ (0x07 if (crc & 0x80) else 0) self._crc8Encode[byte] = crc & 0xFF self._logger.debug('calculated value {0} for byte {1}'.format(self._crc8Encode[byte], byte)) # ##### # This method calculate the CRC8 of a byte array and return the result def calc(self, byte): crc = 0 for c in byte: crcByte = crc & 0xFF ^ c & 0xFF if self._crc8Encode[crcByte] == -1: self._calculCRC8(crcByte) crc = self._crc8Encode[crcByte] self._logger.debug('message : {0} ; crc : {1}'.format(byte, crc)) return crc # ##### # This method convert any type of message to a byte array and return the result of # CRC8.calc with this byte array def calcAllTypes(self,msg): byte = [] if type(msg) is list: byte = bytearray(msg) elif type(msg) is int: byte = msg.to_bytes((msg.bit_length() + 7) // 8, "big") else: byte = bytearray(str(msg), 'utf-8') return self.calc(byte) # ##### # This method calculate the CRC8 of the message and return the result of CRC(msg) = crc def check(self, msg, crc): self._logger.debug('message to check : "{0}" against {1}'.format(msg, crc)) test = self.calcAllTypes(msg) return (test == crc)
import sys from rhum.rhumlogging import get_logger from rhum.rhum import RhumDaemon _pid_file = '/tmp/daemon-rhum.pid' if __name__ == "__main__": logger = get_logger('RHUM-MAIN') logger.debug('rhum service manager : ') daemon = RhumDaemon(_pid_file) if len(sys.argv) == 2: if 'start' == sys.argv[1]: logger.info("Starting Rhum") daemon.start() elif 'stop' == sys.argv[1]: logger.info("Stopping Rhum") daemon.stop() elif 'restart' == sys.argv[1]: logger.info("Restarting Rhum") daemon.restart() elif 'reload' == sys.argv[1]: logger.info("Reloading Rhum") daemon.reload() else: logger.error("Unknow command of Rhum service") sys.exit(2) sys.exit(0) else: print("usage: {0} start|stop|restart".format(sys.argv[0])) sys.exit(2)
class EnOceanDriver(Driver): _logger = get_logger('rhum.driver.enocean.EnOceanDriver') def __init__(self, port='/dev/ttyAMA0', callback=None): super(EnOceanDriver, self).__init__(callback) # Initialize serial port self.__buffer = [] self.__port = port self._logger.debug('initialize connection to '.format(port)) self.__connection = serial.Serial(self.__port, 57600, timeout=0) def stop(self): Driver.stop(self) self.__connection.close() self._logger.info('EnOcean Driver on {0} stopped'.format(self.__port)) def run(self): self._logger.info('EnOcean Driver started on {0}'.format(self.__port)) while not self._stop.is_set(): # Read chars from serial port as hex numbers try: msg = self.parse() __type, __datas, __opts = msg._get() msg = TypingMessage.transform(__type, __datas, __opts) self._logger.info(msg) except serial.SerialException: self._logger.error('Serial port exception! (device disconnected or multiple access on port?)') break except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() lines = traceback.format_exception(exc_type, exc_value, exc_traceback) for line in lines: self._logger.error(line) def test(self): msg = EnOceanMessage(PacketType.COMMON_COMMAND.value, [CommonCommandType.CD_R_VERSION.value]) buffer = msg.build() self._logger.debug('EnOcean Driver message {0}'.format(buffer)) self._logger.debug(self.__connection.isOpen()) #for index in range(len(buffer)): #byte by byte tx buffer = bytes(buffer) self._logger.debug('writing byte {0}'.format(buffer)) self.__connection.write(buffer) try: self._logger.debug('ask for parsing data') msg = self.parse() msg = VersionMessage(msg._get()[0], msg._get()[1], msg._get()[2]) self._logger.info('EnOcean Test Message (Version)') self._logger.info(msg) if msg.isResponse() and msg.getReturnCode() == ResponseType.RET_OK: return True except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() lines = traceback.format_exception(exc_type, exc_value, exc_traceback) for line in lines: self._logger.error(line) self.__connection.close() return False def parse(self): Driver.parse(self) self._logger.debug('parsing data') msg = self._getSerialData() if isinstance(msg, EnOceanMessage): return msg raise Exception('No message parsed') def _getSerialData(self): self._logger.debug('searching for sync byte') s = 0 while s != b'\x55': if self.__connection.inWaiting() != 0: s = self.__connection.read(1) self._logger.debug('sync byte found') while self.__connection.inWaiting() < 5: () header = self.__connection.read(4) #read header fields headerCRC = self.__connection.read(1)[0] #read header crc field self._logger.debug('header reading : {0} and crc : {1}'.format(header, headerCRC)) if (CRC8Utils.calc(header) == headerCRC): self._logger.debug('header CRC OK') data_length, opt_length, msgType = struct.unpack("!HBB", header) self._logger.debug('data_length {0}; opt_length {1}; msg_type {2}'.format( data_length, opt_length, msgType )) totalDataLength = data_length + opt_length while self.__connection.inWaiting() < totalDataLength+1: () datas = self.__connection.read(data_length) opts = self.__connection.read(opt_length) dataCRC = self.__connection.read(1) self._logger.debug('datas {0}; opts {1}; dataCRC {2}'.format( datas, opts, dataCRC )) if(self._logger.isEnabledFor(logging.DEBUG)): msg = header msg += bytes({headerCRC}) msg += datas msg += opts msg += dataCRC self._logger.debug(msg) if (CRC8Utils.calc(datas+opts) == dataCRC[0]): return EnOceanMessage(msgType, datas, opts) return "Data CRC Failed" return "Header CRC Failed"