void HomeConnectDeviceOven::handleEventTypeNotify(const string& aKey, JsonObjectPtr aValue) { ALOG(LOG_INFO, "Oven Event 'NOTIFY' - item: %s, %s", aKey.c_str(), aValue ? aValue->c_strValue() : "<none>"); if (aKey == "Cooking.Oven.Option.SetpointTemperature") { int32_t value = (aValue != NULL) ? aValue->int32Value() : 0; targetTemperatureProp->setInt32Value(value); return; } inherited::handleEventTypeNotify(aKey, aValue); }
void HueApiOperation::processAnswer(JsonObjectPtr aJsonResponse, ErrorPtr aError) { error = aError; if (Error::isOK(error)) { // pre-process response in case of non-GET if (method!=httpMethodGET) { // Expected: // [{"error":{"type":xxx,"address":"yyy","description":"zzz"}}] // or // [{"success": { "xxx": "xxxxxxxx" }] int errCode = HueCommErrorInvalidResponse; string errMessage = "invalid response"; for (int i=0; i<aJsonResponse->arrayLength(); i++) { JsonObjectPtr responseItem = aJsonResponse->arrayGet(i); responseItem->resetKeyIteration(); JsonObjectPtr responseParams; string statusToken; if (responseItem->nextKeyValue(statusToken, responseParams)) { if (statusToken=="success" && responseParams) { // apparently successful, return entire response // Note: use getSuccessItem() to get success details data = aJsonResponse; errCode = HueCommErrorOK; // ok break; } else if (statusToken=="error" && responseParams) { // make Error object out of it JsonObjectPtr e = responseParams->get("type"); if (e) errCode = e->int32Value(); e = responseParams->get("description"); if (e) errMessage = e->stringValue(); break; } } } // for if (errCode!=HueCommErrorOK) { error = ErrorPtr(new HueCommError(errCode, errMessage)); } } else { // GET, just return entire data data = aJsonResponse; } } // done completed = true; // have queue reprocessed hueComm.processOperations(); }
// access to plan44 extras that are not part of the vdc API ErrorPtr P44VdcHost::processP44Request(JsonCommPtr aJsonComm, JsonObjectPtr aRequest) { ErrorPtr err; JsonObjectPtr m = aRequest->get("method"); if (!m) { err = Error::err<P44VdcError>(400, "missing 'method'"); } else { string method = m->stringValue(); if (method=="learn") { // check proximity check disabling bool disableProximity = false; JsonObjectPtr o = aRequest->get("disableProximityCheck"); if (o) { disableProximity = o->boolValue(); } // get timeout o = aRequest->get("seconds"); int seconds = 30; // default to 30 if (o) seconds = o->int32Value(); if (seconds==0) { // end learning prematurely stopLearning(); MainLoop::currentMainLoop().cancelExecutionTicket(learnIdentifyTicket); // - close still running learn request if (learnIdentifyRequest) { learnIdentifyRequest->closeConnection(); learnIdentifyRequest.reset(); } // - confirm abort with no result sendCfgApiResponse(aJsonComm, JsonObjectPtr(), ErrorPtr()); } else { // start learning learnIdentifyRequest = aJsonComm; // remember so we can cancel it when we receive a separate cancel request startLearning(boost::bind(&P44VdcHost::learnHandler, this, aJsonComm, _1, _2), disableProximity); learnIdentifyTicket = MainLoop::currentMainLoop().executeOnce(boost::bind(&P44VdcHost::learnHandler, this, aJsonComm, false, Error::err<P44VdcError>(408, "learn timeout")), seconds*Second); } } else if (method=="identify") { // get timeout JsonObjectPtr o = aRequest->get("seconds"); int seconds = 30; // default to 30 if (o) seconds = o->int32Value(); if (seconds==0) { // end reporting user activity setUserActionMonitor(NULL); MainLoop::currentMainLoop().cancelExecutionTicket(learnIdentifyTicket); // - close still running identify request if (learnIdentifyRequest) { learnIdentifyRequest->closeConnection(); learnIdentifyRequest.reset(); } // - confirm abort with no result sendCfgApiResponse(aJsonComm, JsonObjectPtr(), ErrorPtr()); } else { // wait for next user activity learnIdentifyRequest = aJsonComm; // remember so we can cancel it when we receive a separate cancel request setUserActionMonitor(boost::bind(&P44VdcHost::identifyHandler, this, aJsonComm, _1)); learnIdentifyTicket = MainLoop::currentMainLoop().executeOnce(boost::bind(&P44VdcHost::identifyHandler, this, aJsonComm, DevicePtr()), seconds*Second); } } else if (method=="logLevel") { // get or set logging level for vdcd JsonObjectPtr o = aRequest->get("value"); if (o) { // set new value first int newLevel = o->int32Value(); int oldLevel = LOGLEVEL; SETLOGLEVEL(newLevel); LOG(LOG_WARNING, "\n\n========== changed log level from %d to %d ===============", oldLevel, newLevel); } // anyway: return current value sendCfgApiResponse(aJsonComm, JsonObject::newInt32(LOGLEVEL), ErrorPtr()); } else { err = Error::err<P44VdcError>(400, "unknown method"); } } return err; }
void JsonRpcComm::gotJson(ErrorPtr aError, JsonObjectPtr aJsonObject) { JsonRpcCommPtr keepMeAlive(this); // make sure this object lives until routine terminates ErrorPtr respErr; bool safeError = false; // set when reporting error is safe (i.e. not error possibly generated by malformed error, to prevent error loops) JsonObjectPtr idObj; const char *idString = NULL; if (Error::isOK(aError)) { // received proper JSON, now check JSON-RPC specifics FOCUSLOG("Received JSON message:\n %s\n", aJsonObject->c_strValue()); if (aJsonObject->isType(json_type_array)) { respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - batch mode not supported by this implementation")); } else if (!aJsonObject->isType(json_type_object)) { respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - request must be JSON object")); } else { // check request object fields const char *method = NULL; JsonObjectPtr o = aJsonObject->get("jsonrpc"); if (!o) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - missing 'jsonrpc'")); else if (o->stringValue()!="2.0") respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - wrong version in 'jsonrpc'")); else { // get ID param (must be present for all messages except notification) idObj = aJsonObject->get("id"); if (idObj) idString = idObj->c_strValue(); JsonObjectPtr paramsObj = aJsonObject->get("params"); // JSON-RPC version is correct, check other params method = aJsonObject->getCString("method"); if (method) { // this is a request (responses don't have the method member) safeError = idObj!=NULL; // reporting error is safe if this is a method call. Other errors are reported only when reportAllErrors is set if (*method==0) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - empty 'method'")); else { // looks like a valid method or notification call if (!jsonRequestHandler) { // no handler -> method cannot be executed respErr = ErrorPtr(new JsonRpcError(JSONRPC_METHOD_NOT_FOUND, "Method not found")); } else { if (paramsObj && !paramsObj->isType(json_type_array) && !paramsObj->isType(json_type_object)) { // invalid param object respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - 'params' must be object or array")); } else { // call handler to execute method or notification jsonRequestHandler(method, idString, paramsObj); } } } } else { // this is a response (requests always have a method member) // - check if result or error JsonObjectPtr respObj; if (!aJsonObject->get("result", respObj)) { // must be error, need further decoding respObj = aJsonObject->get("error"); if (!respObj) respErr = ErrorPtr(new JsonRpcError(JSONRPC_INTERNAL_ERROR, "Internal JSON-RPC error - response with neither 'result' nor 'error'")); else { // dissect error object ErrorCode errCode = JSONRPC_INTERNAL_ERROR; // Internal RPC error const char *errMsg = "malformed Error response"; // - try to get error code JsonObjectPtr o = respObj->get("code"); if (o) errCode = o->int32Value(); // - try to get error message o = respObj->get("message"); if (o) errMsg = o->c_strValue(); // compose error object from this respErr = ErrorPtr(new JsonRpcError(errCode, errMsg)); // also get optional data element respObj = respObj->get("data"); } } // Now we have either result or error.data in respObj, and respErr is Ok or contains the error code + message if (!idObj) { // errors without ID cannot be associated with calls made earlier, so just log the error LOG(LOG_WARNING,"JSON-RPC 2.0 warning: Received response with no or NULL 'id' that cannot be dispatched:\n %s\n", aJsonObject->c_strValue()); } else { // dispatch by ID uint32_t requestId = idObj->int32Value(); PendingAnswerMap::iterator pos = pendingAnswers.find(requestId); if (pos==pendingAnswers.end()) { // errors without ID cannot be associated with calls made earlier, so just log the error LOG(LOG_WARNING,"JSON-RPC 2.0 error: Received response with unknown 'id'=%d : %s\n", requestId, aJsonObject->c_strValue()); } else { // found callback JsonRpcResponseCB cb = pos->second; pendingAnswers.erase(pos); // erase cb(requestId, respErr, respObj); // call } respErr.reset(); // handled } } } } } else { // no proper JSON received, create error response if (aError->isDomain(JsonError::domain())) { // some kind of parsing error respErr = ErrorPtr(new JsonRpcError(JSONRPC_PARSE_ERROR, aError->description())); } else { // some other type of server error respErr = ErrorPtr(new JsonRpcError(JSONRPC_SERVER_ERROR, aError->description())); } } // auto-generate error response for internally created errors if (!Error::isOK(respErr)) { if (safeError || reportAllErrors) sendError(idString, respErr); else LOG(LOG_WARNING,"Received data that generated error which can't be sent back: Code=%d, Message='%s'\n", respErr->getErrorCode(), respErr->description().c_str()); } }