homeos/app/Support/Mqtt/ShellyNormalizer.php

66 lines
2.7 KiB
PHP

<?php
namespace App\Support\Mqtt;
/**
* Normalizes a Shelly Gen2+ status payload for one component into protocol-agnostic
* entity updates ({key, type, state}). One message can update several entities
* (e.g. a switch also carries power). Vendor specifics stay here (H3).
*/
class ShellyNormalizer
{
/**
* Entity types that identify a real device component (vs. housekeeping topics like sys/wifi).
* A new prefix is auto-onboarded only when one of these arrives, so `sys`/`cloud`/`mqtt`
* status noise never creates a junk device.
*/
public const PRIMARY_TYPES = ['switch', 'light', 'cover', 'contact', 'temperature', 'humidity', 'battery', 'power', 'motion'];
/**
* @param array<string,mixed> $payload
* @return array<int, array{key:string,type:string,state:array<string,mixed>}>
*/
public static function normalize(string $component, array $payload): array
{
[$kind, $index] = array_pad(explode(':', $component, 2), 2, '0');
$updates = [];
switch ($kind) {
case 'switch':
$updates[] = ['key' => "switch:{$index}", 'type' => 'switch', 'state' => ['on' => (bool) ($payload['output'] ?? false)]];
if (array_key_exists('apower', $payload)) {
$updates[] = ['key' => "power:{$index}", 'type' => 'power', 'state' => ['watts' => (int) round((float) $payload['apower'])]];
}
break;
case 'light':
$updates[] = ['key' => "light:{$index}", 'type' => 'light', 'state' => ['on' => (bool) ($payload['output'] ?? $payload['ison'] ?? false)]];
break;
case 'cover':
$updates[] = ['key' => "cover:{$index}", 'type' => 'cover', 'state' => ['position' => $payload['current_pos'] ?? null, 'state' => $payload['state'] ?? null]];
break;
case 'temperature':
$updates[] = ['key' => "temperature:{$index}", 'type' => 'temperature', 'state' => ['celsius' => $payload['tC'] ?? null]];
break;
case 'humidity':
$updates[] = ['key' => "humidity:{$index}", 'type' => 'humidity', 'state' => ['percent' => $payload['rh'] ?? null]];
break;
case 'devicepower':
if (($pct = data_get($payload, 'battery.percent')) !== null) {
$updates[] = ['key' => "battery:{$index}", 'type' => 'battery', 'state' => ['percent' => (int) $pct]];
}
break;
default:
// Unknown component — store the raw payload under its own key.
$updates[] = ['key' => $component, 'type' => $kind, 'state' => $payload];
}
return $updates;
}
}