Lines
94.64%
53 / 56
Functions and Methods
66.66%
6 / 9
Classes and Traits
0.00%
0 / 1
| Name | Lines | Functions and Methods | CRAP | Classes and Traits | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ExposedEntityDefinition | 94.64% | 53 / 56 | 66.66% | 6 / 9 | 24.09 | 0.00% | 0 / 1 | |||
| __construct | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | |||||
| fromConfig | 92.85% | 13 / 14 | 0.00% | 0 / 1 | 5.01 | |||||
| withConfigOf | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | |||||
| isAction | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | |||||
| toMessageFields | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 2 | |||||
| listCommonFields | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 2 | |||||
| listPlatformFields | 100.00% | 20 / 20 | 100.00% | 1 / 1 | 9 | |||||
| listActionFields | 100.00% | 11 / 11 | 100.00% | 1 / 1 | 2 | |||||
| dropNullFields | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | |||||
| 1 | <?php | |
| 2 | ||
| 3 | declare(strict_types=1); | |
| 4 | ||
| 5 | namespace Stewart\Client\Component; | |
| 6 | ||
| 7 | use LogicException; | |
| 8 | use Stewart\Contracts\Exposure\Action\ActionConfig; | |
| 9 | use Stewart\Contracts\Exposure\Action\ActionField; | |
| 10 | use Stewart\Contracts\Exposure\BinarySensorConfig; | |
| 11 | use Stewart\Contracts\Exposure\ButtonConfig; | |
| 12 | use Stewart\Contracts\Exposure\DateConfig; | |
| 13 | use Stewart\Contracts\Exposure\DateTimeConfig; | |
| 14 | use Stewart\Contracts\Exposure\DeviceInfo; | |
| 15 | use Stewart\Contracts\Exposure\ExposedEntityConfig; | |
| 16 | use Stewart\Contracts\Exposure\ExposedPlatform; | |
| 17 | use Stewart\Contracts\Exposure\ExposureConfig; | |
| 18 | use Stewart\Contracts\Exposure\NumberConfig; | |
| 19 | use Stewart\Contracts\Exposure\SelectConfig; | |
| 20 | use Stewart\Contracts\Exposure\SensorConfig; | |
| 21 | use Stewart\Contracts\Exposure\SwitchConfig; | |
| 22 | use Stewart\Contracts\Exposure\TextConfig; | |
| 23 | use Stewart\Contracts\Exposure\TimeConfig; | |
| 24 | ||
| 25 | final readonly class ExposedEntityDefinition | |
| 26 | { | |
| 27 | /** | |
| 28 | * @param array<string, mixed> $config | |
| 29 | * @param array<string, string>|null $device | |
| 30 | */ | |
| 31 | public function __construct( | |
| 32 | public ExposedPlatform $platform, | |
| 33 | public array $config, | |
| 34 | public ?array $device = null, | |
| 35 | ) {} | |
| 36 | ||
| 37 | public static function fromConfig(ExposureConfig $config, ?DeviceInfo $device): self | |
| 38 | { | |
| 39 | $configFields = match (true) { | |
| 40 | $config instanceof ExposedEntityConfig => [...self::listCommonFields($config), ...self::listPlatformFields($config)], | |
| 41 | $config instanceof ActionConfig => self::listActionFields($config), | |
| 42 | default => throw new LogicException(\sprintf('%s has no component encoding.', $config::class)), | |
| 43 | }; | |
| 44 | ||
| 45 | return new self( | |
| 46 | $config->getPlatform(), | |
| 47 | self::dropNullFields($configFields), | |
| 48 | $device === null ? null : self::dropNullFields([ | |
| 49 | 'identifier' => $device->identifier, | |
| 50 | 'name' => $device->name, | |
| 51 | 'manufacturer' => $device->manufacturer, | |
| 52 | 'model' => $device->model, | |
| 53 | 'suggested_area' => $device->suggestedArea, | |
| 54 | ]), | |
| 55 | ); | |
| 56 | } | |
| 57 | ||
| 58 | public function withConfigOf(self $other): self | |
| 59 | { | |
| 60 | return new self($other->platform, $other->config, $this->device); | |
| 61 | } | |
| 62 | ||
| 63 | public function isAction(): bool | |
| 64 | { | |
| 65 | return $this->platform === ExposedPlatform::Action; | |
| 66 | } | |
| 67 | ||
| 68 | /** @return array<string, mixed> */ | |
| 69 | public function toMessageFields(): array | |
| 70 | { | |
| 71 | return ['platform' => $this->platform->value, 'config' => $this->config, ...($this->device === null ? [] : ['device' => $this->device])]; | |
| 72 | } | |
| 73 | ||
| 74 | /** @return array<string, mixed> */ | |
| 75 | private static function listCommonFields(ExposedEntityConfig $config): array | |
| 76 | { | |
| 77 | return [ | |
| 78 | 'name' => $config->name, | |
| 79 | 'icon' => $config->icon, | |
| 80 | 'entity_category' => $config->entityCategory?->value, | |
| 81 | 'enabled_by_default' => $config->enabledByDefault ? null : false, | |
| 82 | ]; | |
| 83 | } | |
| 84 | ||
| 85 | /** @return array<string, mixed> */ | |
| 86 | private static function listPlatformFields(ExposedEntityConfig $config): array | |
| 87 | { | |
| 88 | return match (true) { | |
| 89 | $config instanceof SensorConfig => [ | |
| 90 | 'device_class' => $config->deviceClass?->value, | |
| 91 | 'unit_of_measurement' => $config->unit, | |
| 92 | 'state_class' => $config->stateClass?->value, | |
| 93 | 'suggested_display_precision' => $config->displayPrecision, | |
| 94 | 'options' => $config->options === [] ? null : $config->options, | |
| 95 | ], | |
| 96 | $config instanceof BinarySensorConfig, $config instanceof SwitchConfig, $config instanceof ButtonConfig => ['device_class' => $config->deviceClass?->value], | |
| 97 | $config instanceof NumberConfig => [ | |
| 98 | 'min' => $config->min, | |
| 99 | 'max' => $config->max, | |
| 100 | 'step' => $config->step, | |
| 101 | 'mode' => $config->mode->value, | |
| 102 | 'device_class' => $config->deviceClass?->value, | |
| 103 | 'unit_of_measurement' => $config->unit, | |
| 104 | ], | |
| 105 | $config instanceof SelectConfig => ['options' => $config->options], | |
| 106 | $config instanceof TextConfig => ['min' => $config->min, 'max' => $config->max, 'pattern' => $config->pattern, 'mode' => $config->mode->value], | |
| 107 | $config instanceof TimeConfig, $config instanceof DateConfig, $config instanceof DateTimeConfig => [], | |
| 108 | default => throw new LogicException(\sprintf('%s has no component encoding.', $config::class)), | |
| 109 | }; | |
| 110 | } | |
| 111 | ||
| 112 | /** @return array<string, mixed> */ | |
| 113 | private static function listActionFields(ActionConfig $config): array | |
| 114 | { | |
| 115 | return [ | |
| 116 | 'name' => $config->name, | |
| 117 | 'description' => $config->description, | |
| 118 | 'fields' => array_map(static fn(ActionField $field): array => self::dropNullFields([ | |
| 119 | 'name' => $field->name, | |
| 120 | 'type' => $field->type->value, | |
| 121 | 'required' => $field->required, | |
| 122 | 'description' => $field->description, | |
| 123 | 'options' => $field->options === [] ? null : $field->options, | |
| 124 | ]), $config->fields), | |
| 125 | ]; | |
| 126 | } | |
| 127 | ||
| 128 | /** | |
| 129 | * @template T | |
| 130 | * | |
| 131 | * @param array<string, T|null> $fields | |
| 132 | * @return array<string, T> | |
| 133 | */ | |
| 134 | private static function dropNullFields(array $fields): array | |
| 135 | { | |
| 136 | return array_filter($fields, static fn(mixed $value): bool => $value !== null); | |
| 137 | } | |
| 138 | } |