def _cb_power_switch(self, oid: int, value: Union[str, float, bool]) -> None: ''' Callback for power switch, both inventory and metrics. ''' try: # rb485.u_l_grid[0] if oid == 0x93F976AB: self.readings.power_switch_readings.grid_voltage_l1 = ensure_type(value, float) # rb485.u_l_grid[1] elif oid == 0x7A9091EA: self.readings.power_switch_readings.grid_voltage_l2 = ensure_type(value, float) # rb485.u_l_grid[3] elif oid == 0x21EE7CBB: self.readings.power_switch_readings.grid_voltage_l3 = ensure_type(value, float) # rb485.f_grid[0] elif oid == 0x9558AD8A: self.readings.power_switch_readings.grid_frequency_l1 = ensure_type(value, float) # rb485.f_grid[1] elif oid == 0xFAE429C5: self.readings.power_switch_readings.grid_frequency_l2 = ensure_type(value, float) # rb485.f_grid[2] elif oid == 0x104EB6A: self.readings.power_switch_readings.grid_frequency_l3 = ensure_type(value, float) # rb485.f_wr[0] elif oid == 0x3B5F6B9D: self.readings.power_switch_readings.power_storage_frequency_l1 = ensure_type(value, float) # rb485.f_wr[1] elif oid == 0x6FD36B32: self.readings.power_switch_readings.power_storage_frequency_l2 = ensure_type(value, float) # rb485.f_wr[2] elif oid == 0x905F707B: self.readings.power_switch_readings.power_storage_frequency_l3 = ensure_type(value, float) else: log.warning('_cb_power_switch: unhandled oid 0x%X', oid) except TypeError: # oid is known at this point, as the else above catches unknowns without raising log.warning('Got wrong type %s for %s', type(value), R.get_by_id(oid).name)
def __post_init__(self): self.object_infos = [ REGISTRY.get_by_name(object_name) for object_name in self.object_names ]
def main(): ''' Main program ''' packets = rdpcap(sys.argv[1]) streams = dict() i = 0 for name, stream in packets.sessions().items(): print(f'Stream {i:4} {name} {stream} ', end='') length = 0 streams[i] = dict() for k in stream: if TCP in k: if len(k[TCP].payload) > 0: if k[TCP].sport == 8899 or k[TCP].dport == 8899: payload = bytes(k[TCP].payload) # skip AT+ keepalive and app serial "protocol switch" '2b3ce1' if payload in [b'AT+\r', b'+<\xe1']: continue ptime = float(k.time) if ptime not in streams[i]: streams[i][ptime] = b'' streams[i][ptime] += payload length += len(payload) print(f'{length} bytes') i += 1 frame = None sid = 0 for _, data in streams.items(): print(f'\nNEW STREAM #{sid}\n') for timestamp, data_item in data.items(): print(f'NEW INPUT: {datetime.fromtimestamp(timestamp):%Y-%m-%d %H:%M:%S.%f} | {data_item.hex()}') # frames should not cross segments (though it may be valid, but the devices haven't been observed doing # that). Sometimes, the device sends invalid data with a very high length field, causing the code to read # way byond the end of the actual data, causing it to miss frames until its length is satisfied. This way, # if the next segment starts with the typical 0x002b used by the devices, the current frame is dropped. # This way only on segment is lost. if frame and data_item[0:2] == b'\0+': print('Frame not complete at segment start, starting new frame.') print(f'command: {frame.command}, length: {frame.frame_length}, oid: 0x{frame.id:X}') frame = None while len(data_item) > 0: if not frame: frame = ReceiveFrame() try: i = frame.consume(data_item) except InvalidCommand as exc: if frame.command == Command.EXTENSION: print('Frame is an extension frame and we don\'t know how to parse it') else: print(f'Invalid command 0x{exc.command:x} received after consuming {exc.consumed_bytes} bytes') i = exc.consumed_bytes except FrameCRCMismatch as exc: print(f'CRC mismatch, got 0x{exc.received_crc:X} but calculated ' f'0x{exc.calculated_crc:X}. Buffer: {frame._buffer.hex()}') i = exc.consumed_bytes except struct.error as exc: print(f'skipping 2 bytes ahead as struct could not unpack: {str(exc)}') i = 2 frame = ReceiveFrame() data_item = data_item[i:] print(f'frame consumed {i} bytes, {len(data_item)} remaining') if frame.complete(): if frame.id == 0: print(f'Frame complete: {frame} Buffer: {frame._buffer.hex()}') else: print(f'Frame complete: {frame}') try: rid = R.get_by_id(frame.id) except KeyError: print('Could not find ID in registry') else: if frame.command == Command.READ: print(f'Received read : {rid.name:40}') else: if frame.command in [Command.RESPONSE, Command.LONG_RESPONSE]: dtype = rid.response_data_type else: dtype = rid.request_data_type is_write = frame.command in [Command.WRITE, Command.LONG_WRITE] try: value = decode_value(dtype, frame.data) except (struct.error, UnicodeDecodeError) as exc: print(f'Could not decode value: {str(exc)}') if is_write: print(f'Received write : {rid.name:40} type: {dtype.name:17} value: UNKNOWN') else: print(f'Received reply : {rid.name:40} type: {dtype.name:17} value: UNKNOWN') except KeyError: print('Could not decode unknown type') if is_write: print(f'Received write : {rid.name:40} value: 0x{frame.data.hex()}') else: print(f'Received reply : {rid.name:40} value: 0x{frame.data.hex()}') else: if dtype == DataType.ENUM: try: value = rid.enum_str(value) except RctClientException as exc: print(f'ENUM mapping failed: {str(exc)}') except KeyError: print('ENUM value out of bounds') if is_write: print(f'Received write : {rid.name:40} type: {dtype.name:17} value: {value}') else: print(f'Received reply : {rid.name:40} type: {dtype.name:17} value: {value}') frame = None print() print('END OF INPUT-SEGMENT') sid += 1
def main(): packets = rdpcap(sys.argv[1]) streams = dict() i = 0 pl = b'' for name, stream in packets.sessions().items(): print(f'Stream {i:4} {name} {stream} ', end='') length = 0 streams[i] = dict() for k in stream: if TCP in k: if len(k[TCP].payload) > 0: if k[TCP].sport == 8899 or k[TCP].dport == 8899: payload = bytes(k[TCP].payload) # skip AT+ keepalive and app serial "protocol switch" if payload == b'AT+\r' or payload == bytearray.fromhex( '2b3ce1'): continue ptime = float(k.time) if ptime not in streams[i]: streams[i][ptime] = b'' streams[i][ptime] += payload length += len(payload) print(f'{length} bytes') i += 1 frame = None for _, data in streams.items(): for ts, pl in data.items(): while len(pl) > 0: if not frame: frame = ReceiveFrame() try: i = frame.consume(pl) except FrameCRCMismatch as exc: if frame.command == Command.EXTENSION: print( 'Frame is an extension frame and we don\'t know how to parse it' ) else: print( f'Frame {frame.id} CRC mismatch, got 0x{exc.received_crc:X} but calculated ' f'0x{exc.calculated_crc:X}. Buffer: {frame._buffer.hex()}' ) print(pl[0:2].hex()) if pl[0:2] == bytearray.fromhex('002b'): i = 2 else: i = exc.consumed_bytes pl = pl[i:] print(f'frame consumed {i} bytes, {len(pl)} remaining') if frame.complete(): if frame.id == 0: print( f'Frame complete: {frame} Buffer: {frame._buffer.hex()}' ) else: print(f'Frame complete: {frame}') try: rid = R.get_by_id(frame.id) except KeyError: print('Could not find ID in registry') else: if frame.command == Command.READ: print( f'Received read : {rid.index:4} {rid.name:40}') else: if frame.command in [ Command.RESPONSE, Command.LONG_RESPONSE ]: dtype = rid.response_data_type else: dtype = rid.request_data_type try: value = decode_value(dtype, frame.data) except struct.error as exc: print(f'Could not decode value: {str(exc)}') print( f'Received reply: {rid.index:4} {rid.name:40} type: {dtype.name:17} value: ' 'UNKNOWN') except UnicodeDecodeError as exc: print(f'Could not decode value: {str(exc)}') print( f'Received reply: {rid.index:4} {rid.name:40} type: {dtype.name:17} value: ' 'UNKNOWN') else: if dtype == DataType.ENUM: try: value = rid.enum_str(value) except RctClientException as exc: print( f'ENUM mapping failed: {str(exc)}') except KeyError: print('ENUM value out of bounds') print( f'Received reply: {rid.index:4} {rid.name:40} type: {dtype.name:17} value: ' f'{value}') frame = None
async def _write_object(self, reader: StreamReader, writer: StreamWriter, object_id: int, value): oinfo = REGISTRY.get_by_id(object_id) object_name = oinfo.name payload = encode_value(oinfo.request_data_type, value) send_command_frame = SendFrame(command=Command.WRITE, id=object_id, payload=payload) self.logger.debug( "Writing RCT Power data (%s) for object %x (%s)...", str(value), object_id, object_name, ) request_time = datetime.now() try: async with async_timeout.timeout(READ_TIMEOUT): await writer.drain() writer.write(send_command_frame.data) # loop until we return or time out while True: response_frame = ReceiveFrame() while not response_frame.complete() and not reader.at_eof( ): raw_response = await reader.read(1) if len(raw_response) > 0: response_frame.consume(raw_response) if response_frame.complete(): response_object_info = REGISTRY.get_by_id( response_frame.id) data_type = response_object_info.response_data_type received_object_name = response_object_info.name # ignore, if this is not the answer to the latest request if object_id != response_frame.id: self.logger.debug( "Mismatch of requested and received object ids: requested %x (%s), but received %x (%s)", object_id, object_name, response_frame.id, received_object_name, ) continue decoded_value: Union[ bool, bytes, float, int, str, Tuple[datetime, Dict[datetime, int]], Tuple[datetime, Dict[datetime, EventEntry]], ] = decode_value( data_type, response_frame.data) # type: ignore self.logger.debug( "Decoded data for object %x (%s): %s", response_frame.id, received_object_name, decoded_value, ) return ValidApiResponse( object_id=object_id, object_name=object_name, time=request_time, value=decoded_value, ) else: self.logger.debug( "Error decoding object %x (%s): %s", object_id, object_name, response_frame.data, ) return InvalidApiResponse( object_id=object_id, object_name=object_name, time=request_time, cause="INCOMPLETE", ) except TimeoutError as exc: self.logger.debug("Error reading object %x (%s): %s", object_id, object_name, str(exc)) return InvalidApiResponse( object_id=object_id, object_name=object_name, time=request_time, cause="OBJECT_READ_TIMEOUT", ) except FrameCRCMismatch as exc: self.logger.debug("Error reading object %x (%s): %s", object_id, object_name, str(exc)) return InvalidApiResponse( object_id=object_id, object_name=object_name, time=request_time, cause="CRC_ERROR", ) except FrameLengthExceeded as exc: self.logger.debug("Error reading object %x (%s): %s", object_id, object_name, str(exc)) return InvalidApiResponse( object_id=object_id, object_name=object_name, time=request_time, cause="FRAME_LENGTH_EXCEEDED", ) except InvalidCommand as exc: self.logger.debug("Error reading object %x (%s): %s", object_id, object_name, str(exc)) return InvalidApiResponse( object_id=object_id, object_name=object_name, time=request_time, cause="INVALID_COMMAND", ) except struct.error as exc: self.logger.debug("Error reading object %x (%s): %s", object_id, object_name, str(exc)) return InvalidApiResponse( object_id=object_id, object_name=object_name, time=request_time, cause="PARSING_ERROR", ) except Exception as exc: self.logger.debug("Error reading object %x (%s): %s", object_id, object_name, str(exc)) return InvalidApiResponse( object_id=object_id, object_name=object_name, time=request_time, cause="UNKNOWN_ERROR", )