CoreProtocol::RESP_CODE_NO_MORE_MESSAGES]; case CoreProtocol::RESP_CODE_ADVERT_PATH: return self::parseAdvertPathResponse($payload); case CoreProtocol::RESP_CODE_STATS: return self::parseCodeStatusResponse($payload); case CoreProtocol::RESP_CODE_DEVICE_INFO: return self::parseDeviceInfoResponse($payload); case CoreProtocol::RESP_CODE_CHANNEL_INFO: return self::parseChannelInfoResponse($payload); case CoreProtocol::PUSH_CODE_MSG_WAITING: return ['code'=>CoreProtocol::PUSH_CODE_MSG_WAITING]; case CoreProtocol::RESP_CODE_CONTACT_MSG_RECV_V3: case CoreProtocol::RESP_CODE_CHANNEL_MSG_RECV_V3: case CoreProtocol::RESP_CODE_CHANNEL_MSG_RECV: return $payload; default: echo "Unparsed response: " . \Menking\Meshcore\Util\Debug::hexDump($payload) . "\n"; return $payload; } } /** * * @param string $payload * @return BinaryResponse */ protected static function parseBinaryResponse(string $payload): BinaryResponse { $data = new BinaryResponse(); $data->code = ord($payload[0]); $data->reserved = ord($payload[1]); $data->tag = unpack('V', substr($payload, 2, 4))[1]; $data->lpp = []; $lpp = substr($payload, 6); // Cayenne LPP proto $offset = 0; while($offset < strlen($lpp) ) { $block = substr($lpp, $offset, 4); $q = [ 'channel' => ord($block[0]), 'type' => ord($block[1]), 'type_name'=>CoreProtocol::getLppType(ord($block[1])), 'value' => unpack('n', substr($block, 2, 2))[1] ]; switch($q['type']) { case CoreProtocol::LPP_VOLTAGE: $q['value'] /= 100; break; case CoreProtocol::LPP_TEMP: $q['value'] /= 10; $q['fahrenheit'] = (($q['value'] * 9.0 / 5.0) + 32.0); break; } $offset += 4; if( $q['channel'] != 0 && $q['type'] != 0 ) { $data->lpp[] = $q; } } return $data; } /** * * @param string $payload * @return ErrorResponse */ protected static function parseError(string $payload): ErrorResponse { $e = new ErrorResponse(); $e->code = ord($payload[0]); $e->error_code = ord($payload[1]); $e->error_text = CoreProtocol::getErrorText(ord($payload[1])); return $e; } /** * * @param string $payload * @return CurrentTimeResponse */ protected static function parseCurrentTime(string $payload): CurrentTimeResponse { $data = new CurrentTimeResponse(); $data->code = ord($payload[0]); $data->current_time = unpack('V', substr($payload, 1, 4))[1]; return $data; } /** * * @param string $payload * @return LoginResponse */ protected static function parseLoginFail(string $payload): LoginResponse { $data = new LoginResponse(); $data->code = ord($payload[0]); $data->status = false; $data->is_admin = ord($payload[1]); $data->pub_key_prefix = base64_encode(substr($payload, 2, 6)); return $data; } /** * * @param string $payload * @return LoginResponse */ protected static function parseLoginSuccess(string $payload): LoginResponse { $data = new LoginResponse(); $data->code = ord($payload[0]); $data->status = true; $data->is_admin = ord($payload[1]); $data->pub_key_prefix = base64_encode(substr($payload, 2, 6)); if( strlen($payload) > 8 ) { $data->timestamp = unpack('V', substr($payload, 8, 4))[1]; $data->acl = ord($payload[12]); $data->firmware_version = ord($payload[13]); } return $data; } /** * * @param string $payload * @return LogReceiveResponse */ protected static function parseLogRxData(string $payload): LogReceiveResponse { $data = new LogReceiveResponse(); $data->code = ord($payload[0]); $data->snr = ord($payload[1]) / 4; $data->rssi = (ord($payload[2]) >= 128) ? (ord($payload[2]) - 256): ord($payload[2]); $data->log = base64_encode(substr($payload, 3, strlen($payload))); return $data; } /** * * @param string $payload * @return CodeSentResponse */ protected static function parseCodeSent(string $payload): CodeSentResponse { $data = new CodeSentResponse(); $data->code = ord($payload[0]); $data->sent_as_flood = ord($payload[1]); $data->tag= unpack('V', substr($payload, 2, 4))[1]; $data->est_timeout = unpack('V', substr($payload, 6, 4))[1]; return $data; } /** * * @param string $payload * @return DeviceInfoResponse */ protected static function parseDeviceInfo(string $payload): DeviceInfoResponse { $data = new DeviceInfoResponse(); $data->code = ord($payload[0]); $data->firmware_version = ord($payload[1]); $data->max_contacts_raw = ord($payload[2]); $data->max_channels = ord($payload[3]); $data->ble_pin = unpack('v', substr($payload, 4, 4))[1]; $data->firmware_build = trim(substr($payload, 8, 12)); $data->model = trim(substr($payload, 20, 40)); $data->version = trim(substr($payload, 60, 20)); $data->client_repeat_enabled = ord($payload[80]); $data->path_hash_mode = ord($payload[81]); return $data; } /** * * @param string $payload * @return ChannelResponse */ protected static function parseGetChannel(string $payload): ChannelResponse { $detail = explode("\0", substr($payload, 2, 32)); $data = new ChannelResponse(); $data->code = ord($payload[0]); $data->channel_idx = ord($payload[1]); $data->detail = $detail; $data->channel_secret = base64_encode(substr($payload, 34, 16)); return $data; } /** * * @param string $payload * @return BatteryStorageResponse */ protected static function parseBatteryAndStorage(string $payload): BatteryStorageResponse { $data = new BatteryStorageResponse(); $data->code = ord($payload[0]); $data->battery_millivolt = unpack('v', substr($payload, 1, 2))[1]; $data->storage_used = unpack('V', substr($payload, 3, 4))[1]; $data->storage_total = unpack('V', substr($payload, 7, 4))[1]; return $data; } public static function getContact(string $payload) { $contact = [ 'code'=>ord($payload[0]), 'pub_key'=>base64_encode(substr($payload, 1, CoreProtocol::PUB_KEY_SIZE)), 'type'=>ord($payload[33]), 'flags'=>ord($payload[34]), 'out_path_len'=>ord($payload[35]), 'out_path'=>trim(substr($payload, 36, 64)), 'contact_name'=>trim(substr($payload, 100, 32)), 'last_advert_timestamp'=>date('r', unpack('V', substr($payload, 132, 4))[1]), 'gps_lat'=>unpack('l', substr($payload, 136, 4))[1] / 1000000.0, 'gps_lon'=>unpack('l', substr($payload, 140, 4))[1] / 1000000.0, 'lastmod'=>date('r', unpack('V', substr($payload, 144, 4))[1]) ]; return $contact; } public static function parseAppStart(string $payload) { $info = [ 'response_code'=>ord($payload[0]), 'advert_type'=>ord($payload[1]), 'tx_power_dbm'=>ord($payload[2]), 'max_tx_power'=>ord($payload[3]), 'public_key'=>base64_encode(substr($payload, 4, CoreProtocol::PUB_KEY_SIZE)), 'lat'=>unpack('l', substr($payload, 36, 4))[1] / 1000000.0, 'long'=>unpack('l', substr($payload, 40, 4))[1] / 1000000.0, 'multi_acks'=>ord($payload[44]), 'advert_loc_policy'=>ord($payload[45]), ]; return $info; } public static function parseAdvertPathResponse(string $payload) { $info = [ 'code'=>ord($payload[0]), 'received_timestamp'=>unpack('V', substr($payload, 1, 4))[1], 'path_len'=>ord($payload[5]), 'path'=>substr($payload, 6, ord($payload[5])) ]; return $info; } public static function parseCodeStatusResponse(string $payload) { $info = [ 'code'=>ord($payload[0]), 'type'=>ord($payload[1]), ]; switch($info['type']) { case CoreProtocol::STATS_TYPE_CORE: $info['battery_mv'] = unpack('v', substr($payload, 2, 2))[1]; $info['uptime_secs'] = unpack('V', substr($payload, 4, 4))[1]; $info['err_flags'] = unpack('v', substr($payload, 8, 2))[1]; $info['queue_len'] = ord($payload[10]); break; case CoreProtocol::STATS_TYPE_RADIO: $info['noise_floor'] = unpack('v', substr($payload, 2, 2))[1]; $info['last_rssi'] = (ord($payload[4]) >= 128) ? (ord($payload[4]) - 256): ord($payload[4]); $info['last_snr'] = ord($payload[5]); $info['tx_air_secs'] = unpack('V', substr($payload, 6, 4))[1]; $info['rx_air_secs'] = unpack('V', substr($payload, 10, 4))[1]; break; case CoreProtocol::STATS_TYPE_PACKETS: $info['recv'] = unpack('V', substr($payload, 2, 4))[1]; $info['sent'] = unpack('V', substr($payload, 6, 4))[1]; $info['n_sent_flood'] = unpack('V', substr($payload, 10, 4))[1]; $info['n_sent_direct'] = unpack('V', substr($payload, 14, 4))[1]; $info['n_recv_flood'] = unpack('V', substr($payload, 18, 4))[1]; $info['n_recv_direct'] = unpack('V', substr($payload, 22, 4))[1]; $info['n_rec_errors'] = unpack('V', substr($payload, 26, 4))[1]; break; } return $info; } public static function parseDeviceInfoResponse(string $payload) { $info = [ 'code'=>ord($payload[0]), 'fw_ver_code'=>ord($payload[1]), 'max_contacts'=>ord($payload[2]) * 2, 'max_group_channels'=>ord($payload[3]), 'ble_pin'=>unpack('V', substr($payload, 4, 4))[1], 'fw_build_date'=>trim(substr($payload, 8, 12)), 'manufacturer_name'=>trim(substr($payload, 20, 40)), 'fw_version'=>trim(substr($payload, 60, 20)), 'client_repeat'=>ord($payload[80]), 'path_hash_mode'=>ord($payload[81]) ]; return $info; } public static function parseChannelInfoResponse(string $payload) { $info = [ 'code'=>ord($payload[0]), 'channel_idx'=>ord($payload[1]), 'channel_name'=>self::readCstring(substr($payload, 2, 32)), 'channel_secret'=>substr($payload, 34, 16) ]; return $info; } public static function parseMessage(string $payload) { $data = [ 'code'=>ord($payload[0]), 'channel_idx'=>ord($payload[1]), 'path_len'=>ord($payload[2]), 'type'=>ord($payload[3]), 'timestamp'=>date('r', unpack('V', substr($payload, 4, 4))[1]), 'message'=>substr($payload, 8, strlen($payload)) ]; return $data; } public static function readCstring($str) { $x = strpos($str, "\0"); if( $x === false ) { return null; } else { return trim(substr($str, 0, $x)); } } }