Files
meshcore-php/src/CoreParser.php
T
2026-05-07 14:41:36 -04:00

299 lines
9.7 KiB
PHP

<?php
namespace Menking\Meshcore;
use Exception;
use Menking\Meshcore\Model\BinaryResponse;
use Menking\Meshcore\Model\ChannelResponse;
use Menking\Meshcore\Model\CodeSentResponse;
use Menking\Meshcore\Model\CurrentTimeResponse;
use Menking\Meshcore\Model\DeviceInfoResponse;
use Menking\Meshcore\Model\ErrorResponse;
use Menking\Meshcore\Model\LoginResponse;
use Menking\Meshcore\Model\LogReceiveResponse;
use Menking\Meshcore\Model\BatteryStorageResponse;
class CoreParser {
/**
*
* @param string $payload
* @throws Exception
*/
public static function parseResponse(string $payload): mixed {
switch(ord($payload[0])) {
case CoreProtocol::RESP_CODE_OK:
return true;
case CoreProtocol::RESP_CODE_ERR:
//throw new \Exception("Protocol error: " . CoreProtocol::getErrorText(ord($payload[1])));
return self::parseError($payload);
case CoreProtocol::RESP_CODE_CONTACT:
return self::getContact($payload);
case CoreProtocol::RESP_CODE_SELF_INFO:
return self::parseAppStart($payload);
case CoreProtocol::RESP_CODE_SENT:
return self::parseCodeSent($payload);
case CoreProtocol::PUSH_CODE_LOGIN_SUCCESS:
return self::parseLoginSuccess($payload);
case CoreProtocol::PUSH_CODE_LOG_RX_DATA:
return self::parseLogRxData($payload);
case CoreProtocol::PUSH_CODE_LOGIN_FAIL:
return self::parseLoginFail($payload);
case CoreProtocol::RESP_CODE_CURR_TIME:
return self::parseCurrentTime($payload);
case CoreProtocol::PUSH_CODE_BINARY_RESPONSE:
return self::parseBinaryResponse($payload);
case CoreProtocol::RESP_CODE_BATT_AND_STORAGE:
return self::parseBatteryAndStorage($payload);
case CoreProtocol::RESP_CODE_NO_MORE_MESSAGES:
return (object)['code'=>CoreProtocol::RESP_CODE_NO_MORE_MESSAGES];
default:
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 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;
}
}