async def call(self, *, message: Any, context: RouterContext): ocpp_version = subprotocol_to_ocpp_version(self.subprotocol) camel_case_payload = snake_to_camel_case(asdict(message)) call = Call( unique_id=str(self._unique_id_generator()), action=message.__class__.__name__[:-7], payload=remove_nones(camel_case_payload), ) validate_payload(call, ocpp_version) await self._send(message=call.to_json(), is_response=False, context=context) self.subscriptions[call.unique_id] = context.queue try: response = await asyncio.wait_for(context.queue.get(), self._response_timeout) except asyncio.TimeoutError: del self.subscriptions[call.unique_id] raise if response.message_type_id == MessageType.CallError: log.warning("Received a CALLError: %s'", response) raise response.to_exception() else: response.action = call.action validate_payload(response, ocpp_version) snake_case_payload = camel_to_snake_case(response.payload) call_result = context.ocpp_adapter.call_result cls = getattr(call_result, message.__class__.__name__) return cls(**snake_case_payload)
def message_to_payload(message: str, action: str) -> Any: response = unpack(message) response.action = action validate_payload(response, "1.6") snake_case_payload = camel_to_snake_case(response.payload) cls = getattr(call_result, f"{action}Payload") payload = cls(**snake_case_payload) return payload
def test_camel_to_snake_case(test_input, expected): result = camel_to_snake_case(test_input) assert result == expected
async def _handle_call(self, msg): """ Execute all hooks installed for based on the Action of the message. First the '_on_action' hook is executed and its response is returned to the client. If there is no '_on_action' hook for Action in the message a CallError with a NotImplemtendError is returned. Next the '_after_action' hook is executed. """ try: handlers = self.route_map[msg.action] except KeyError: raise NotImplementedError(f"No handler for '{msg.action}' " "registered.") if not handlers.get("_skip_schema_validation", False): validate_payload(msg, self._ocpp_version) # OCPP uses camelCase for the keys in the payload. It's more pythonic # to use snake_case for keyword arguments. Therefore the keys must be # 'translated'. Some examples: # # * chargePointVendor becomes charge_point_vendor # * firmwareVersion becomes firmwareVersion snake_case_payload = camel_to_snake_case(msg.payload) try: handler = handlers["_on_action"] except KeyError: raise NotImplementedError(f"No handler for '{msg.action}' " "registered.") try: response = handler(**snake_case_payload) if inspect.isawaitable(response): response = await response except Exception as e: LOGGER.exception("Error while handling request '%s'", msg) response = msg.create_call_error(e).to_json() await self._send(response) return temp_response_payload = asdict(response) # Remove nones ensures that we strip out optional arguments # which were not set and have a default value of None response_payload = remove_nones(temp_response_payload) # The response payload must be 'translated' from snake_case to # camelCase. So: # # * charge_point_vendor becomes chargePointVendor # * firmware_version becomes firmwareVersion camel_case_payload = snake_to_camel_case(response_payload) response = msg.create_call_result(camel_case_payload) if not handlers.get("_skip_schema_validation", False): validate_payload(response, self._ocpp_version) response = await self._response_strategy.run(response.to_json()) try: handler = handlers["_after_action"] await self._after_hook_strategy.run(handler, snake_case_payload) except KeyError: # '_on_after' hooks are not required. Therefore ignore exception # when no '_on_after' hook is installed. pass return response
async def _handle_call(self, msg, *, context: RouterContext = None): """ Execute all hooks installed for based on the Action of the message. First the '_on_action' hook is executed and its response is returned to the client. If there is no '_on_action' hook for Action in the message a CallError with a NotImplemtendError is returned. Next the '_after_action' hook is executed. """ ocpp_version = subprotocol_to_ocpp_version(self.subprotocol) try: handlers = self._route_map[msg.action] except KeyError: raise NotImplementedError(f"No handler for '{msg.action}' " "registered.") if not handlers.get("_skip_schema_validation", False): validate_payload(msg, ocpp_version) # OCPP uses camelCase for the keys in the payload. It's more pythonic # to use snake_case for keyword arguments. Therefore the keys must be # 'translated'. Some examples: # # * chargePointVendor becomes charge_point_vendor # * firmwareVersion becomes firmwareVersion snake_case_payload = camel_to_snake_case(msg.payload) try: handler = handlers["_on_action"] except KeyError: raise NotImplementedError(f"No handler for '{msg.action}' " "registered.") handler_context = HandlerContext( charging_station_id=context.charging_station_id, _router_context=context, _router=self, ) # Convert message to correct Call instance class_ = getattr(context.ocpp_adapter.call, f"{msg.action}Payload") payload = class_(**snake_case_payload) try: response = handler(payload=payload, context=handler_context) if inspect.isawaitable(response): response = await response except Exception as e: log.exception("Error while handling request '%s'", msg) response = msg.create_call_error(e).to_json() await self._send(message=response, is_response=True, context=context) temp_response_payload = asdict(response) # Remove nones ensures that we strip out optional arguments # which were not set and have a default value of None response_payload = remove_nones(temp_response_payload) # The response payload must be 'translated' from snake_case to # camelCase. So: # # * charge_point_vendor becomes chargePointVendor # * firmware_version becomes firmwareVersion camel_case_payload = snake_to_camel_case(response_payload) response = msg.create_call_result(camel_case_payload) if not handlers.get("_skip_schema_validation", False): validate_payload(response, ocpp_version) await self._send(message=response.to_json(), is_response=True, context=context) try: handler = handlers["_after_action"] response = handler(payload=payload, context=handler_context) if inspect.isawaitable(response): if self._create_task: # Create task to avoid blocking when making a call # inside the after handler asyncio.ensure_future(response) else: await response except KeyError: # '_on_after' hooks are not required. Therefore ignore exception # when no '_on_after' hook is installed. pass