code = ord($payload[0]); return $m; } protected static function parseMsg(string $payload): mixed { $m = new MessageResponse(); $idx = 0; $m->code = ord($payload[$idx]); $idx++; $m->snr = 0; if( $m->code == CoreProtocol::RESP_CODE_CHANNEL_MSG_RECV_V3 ) { $m->snr = ord($payload) / 4; $idx += 1; // two reserved bytes $idx += 2; } $m->channel_idx = ord($payload[$idx]); $m->path_len = ord($payload[$idx+1]); $m->type = ord($payload[$idx+2]); $m->timestamp = unpack('V', substr($payload, $idx+3, 4))[1]; $m->message = substr($payload, $idx+7); return $m; } /** * * @param string $payload * @return mixed */ protected static function parseAdvert(string $payload): mixed { $m = new AdvertResponse(); $m->code = ord($payload[0]); $m->pub_key = self::formatPublicKey(substr($payload, 1, CoreProtocol::PUB_KEY_SIZE)); if( strlen($payload) > CoreProtocol::PUB_KEY_SIZE + 2 ) { $m->type = ord($payload[33]); $m->flags = ord($payload[34]); $m->out_path = self::formatPublicKey(substr($payload, 35, CoreProtocol::MAX_PATH_SIZE+1)); $m->name = rtrim(substr($payload, 100, 32)); if( strlen($payload) > 132 ) { $m->last_advert_time = unpack('V', substr($payload, 132, 4))[1]; $m->lat = unpack('V', substr($payload, 136, 4))[1]; $m->lon = unpack('V', substr($payload, 140, 4))[1]; $m->last_mod = unpack('V', substr($payload, 144, 4))[1]; } } return $m; } /** * * @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 = self::formatPublicKey(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 = self::formatPublicKey(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->flags = ord($payload[1]); //$data->snr = ord($payload[2]) / 4; $data->raw = 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 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; } /** * * @param string $payload * @return ContactResponse */ public static function getContact(string $payload): ContactResponse { $m = new ContactResponse(); $m->code = ord($payload[0]); $m->pub_key = self::formatPublicKey(substr($payload, 1, CoreProtocol::PUB_KEY_SIZE)); $m->type = ord($payload[33]); $m->flags = ord($payload[34]); $m->out_path_len = ord($payload[35]); $m->out_path = self::formatPublicKey(substr($payload, 36, 64)); $m->name = trim(substr($payload, 100, 32), ' '); $m->last_advert_timestamp = date('r', unpack('V', substr($payload, 132, 4))[1]); $m->lat = unpack('l', substr($payload, 136, 4))[1] / 1000000.0; $m->lon = unpack('l', substr($payload, 140, 4))[1] / 1000000.0; $m->lastmod = date('r', unpack('V', substr($payload, 144, 4))[1]); return $m; } /** * * @param string $payload * @return AppStartResponse */ protected static function parseAppStart(string $payload): AppStartResponse { $m = new AppStartResponse(); $m->code = ord($payload[0]); $m->advert_type = ord($payload[1]); $m->tx_power_dbm = ord($payload[2]); $m->max_tx_power = ord($payload[3]); $m->pub_key = self::formatPublicKey(substr($payload, 4, CoreProtocol::PUB_KEY_SIZE)); $m->lat = unpack('l', substr($payload, 36, 4))[1] / 1000000.0; $m->lon = unpack('l', substr($payload, 40, 4))[1] / 1000000.0; $m->multi_acks = ord($payload[44]); $m->advert_loc_policy = ord($payload[45]); return $m; } /** * * @param string $payload * @return AdvertPathResponse */ protected static function parseAdvertPathResponse(string $payload): AdvertPathResponse { $m = new AdvertPathResponse(); $m->code = ord($payload[0]); $m->received_timestamp = unpack('V', substr($payload, 1, 4))[1]; $m->path_len = ord($payload[5]); $m->path = substr($payload, 6, ord($payload[5])); return $m; } /** * * @param string $payload * @return CodeStatusTypeCoreResponse|CodeStatusTypeRadioResponse|CodeStatusTypePacketResponse */ protected static function parseCodeStatusResponse(string $payload): CodeStatusResponse { $m = null; switch(ord($payload[1])) { case CoreProtocol::STATS_TYPE_CORE: $m = new CodeStatusResponse(); $m->code = ord($payload[0]); $m->type = CodeStatusResponse::RESP_CORE; $m->battery_mv = unpack('v', substr($payload, 2, 2))[1]; $m->uptime_secs = unpack('V', substr($payload, 4, 4))[1]; $m->err_flags = unpack('v', substr($payload, 8, 2))[1]; $m->queue_len = ord($payload[10]); break; case CoreProtocol::STATS_TYPE_RADIO: $m = new CodeStatusResponse(); $m->code = ord($payload[0]); $m->type = CodeStatusResponse::RESP_RADIO; $m->noise_floor = unpack('v', substr($payload, 2, 2))[1]; $m->last_rssi = (ord($payload[4]) >= 128) ? (ord($payload[4]) - 256): ord($payload[4]); $m->last_snr = ord($payload[5]);; $m->tx_air_secs = unpack('V', substr($payload, 6, 4))[1]; $m->rx_air_secs = unpack('V', substr($payload, 10, 4))[1]; break; case CoreProtocol::STATS_TYPE_PACKETS: $m = new CodeStatusResponse(); $m->code = ord($payload[0]); $m->type = CodeStatusResponse::RESP_PACKET; $m->recv = unpack('V', substr($payload, 2, 4))[1]; $m->sent = unpack('V', substr($payload, 6, 4))[1]; $m->n_sent_flood = unpack('V', substr($payload, 10, 4))[1]; $m->n_sent_direct = unpack('V', substr($payload, 14, 4))[1]; $m->n_recv_flood = unpack('V', substr($payload, 18, 4))[1]; $m->n_recv_direct = unpack('V', substr($payload, 22, 4))[1]; $m->n_recv_errors = unpack('V', substr($payload, 26, 4))[1]; break; } return $m; } /** * * @param string $payload * @return DeviceInfoResponse */ protected static function parseDeviceInfoResponse(string $payload): DeviceInfoResponse { $m = new DeviceInfoResponse(); $m->code = ord($payload[0]); $m->firmware_version = ord($payload[1]); $m->max_contacts_raw = ord($payload[2]) * 2; $m->max_channels = ord($payload[3]); $m->ble_pin = unpack('V', substr($payload, 4, 4))[1]; $m->firmware_build = trim(substr($payload, 8, 12)); $m->model = trim(substr($payload, 20, 40)); $m->version =trim(substr($payload, 60, 20)); $m->client_repeat_enabled = ord($payload[80]); $m->path_hash_mode = ord($payload[81]); return $m; } /** * * @param string $payload * @return ChannelResponse */ protected static function parseChannelInfoResponse(string $payload): ChannelResponse { $m = new ChannelResponse(); $m->code = ord($payload[0]); $m->channel_idx = ord($payload[1]); $m->channel_name = self::readCstring(substr($payload, 2, 32)); $m->channel_secret = substr($payload, 34, 16); return $m; } /** * * @param mixed $str * @return null|string */ public static function readCstring($str) { $x = strpos($str, "\0"); if( $x === false ) { return null; } else { return trim(substr($str, 0, $x)); } } /** * * @param string $pubkey * @return string */ public static function formatPublicKey(string $pubkey): string { return bin2hex($pubkey); } }