59 lines
1.9 KiB
PHP
59 lines
1.9 KiB
PHP
<?php
|
|
|
|
namespace App\Support\Mqtt;
|
|
|
|
/**
|
|
* ring-mqtt (tsightler/ring-mqtt) topic conventions. The bridge publishes device state under
|
|
* `ring/<location>/<category>/<deviceId>/<entity>/state` and a bridge health topic under
|
|
* `ring/<location>/status`. Vendor specifics stay here (H3).
|
|
*
|
|
* NOTE: the exact per-entity topic shape should be validated against a live ring-mqtt instance
|
|
* when the user connects their account; the parser is deliberately defensive so unrecognized
|
|
* topics are ignored rather than creating junk devices.
|
|
*/
|
|
class RingTopics
|
|
{
|
|
/** Topics the listener subscribes to. */
|
|
public static function subscriptions(): array
|
|
{
|
|
return ['ring/#'];
|
|
}
|
|
|
|
/**
|
|
* Parse a device state topic.
|
|
*
|
|
* @return array{location:string,category:string,ring_id:string,entity:string}|null
|
|
*/
|
|
public static function parseState(string $topic): ?array
|
|
{
|
|
$parts = explode('/', $topic);
|
|
|
|
if (($parts[0] ?? null) !== 'ring' || end($parts) !== 'state') {
|
|
return null;
|
|
}
|
|
|
|
// ring/<location>/<category>/<deviceId>/<entity>/state
|
|
if (count($parts) === 6) {
|
|
return ['location' => $parts[1], 'category' => $parts[2], 'ring_id' => $parts[3], 'entity' => $parts[4]];
|
|
}
|
|
|
|
// ring/<location>/<category>/<deviceId>/state → device-level; treat category as the entity
|
|
if (count($parts) === 5) {
|
|
return ['location' => $parts[1], 'category' => $parts[2], 'ring_id' => $parts[3], 'entity' => $parts[2]];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* The bridge's own online/offline topic, e.g. "ring/<location>/status".
|
|
* Returns true when this topic is a bridge status topic.
|
|
*/
|
|
public static function isBridgeStatus(string $topic): bool
|
|
{
|
|
$parts = explode('/', $topic);
|
|
|
|
return ($parts[0] ?? null) === 'ring' && count($parts) === 3 && $parts[2] === 'status';
|
|
}
|
|
}
|