Lines 93.33% 14 / 15
Functions and Methods 66.66% 2 / 3
Classes and Traits 0.00% 0 / 1
Name Lines Functions and Methods CRAP Classes and Traits
SensorConfig 93.33% 14 / 15 66.66% 2 / 3 17.09 0.00% 0 / 1
 __construct 100.00% 9 / 9 100.00% 1 / 1 7
 getPlatform 0.00% 0 / 1 0.00% 0 / 1 2
 formatState 100.00% 5 / 5 100.00% 1 / 1 9
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Contracts\Exposure;
6
7use DateTimeInterface;
8use Stewart\Contracts\Exception\ExposureException;
9use Stewart\Contracts\Wire\ListOf;
10
11final readonly class SensorConfig extends ExposedEntityConfig
12{
13    /**
14     * @param list<string> $options
15     * @throws ExposureException
16     */
17    public function __construct(
18        public ?SensorDeviceClass $deviceClass = null,
19        public ?string $unit = null,
20        public ?SensorStateClass $stateClass = null,
21        public ?int $displayPrecision = null,
22        #[ListOf('string')]
23        public array $options = [],
24        ?string $name = null,
25        ?string $icon = null,
26        ?EntityCategory $entityCategory = null,
27        bool $enabledByDefault = true,
28    ) {
29        parent::__construct($name, $icon, $entityCategory, $enabledByDefault);
30
31        if ($unit === '') {
32            throw ExposureException::configInvalid('The sensor unit must not be empty; leave it null for none.');
33        }
34
35        if ($displayPrecision !== null && $displayPrecision < 0) {
36            throw ExposureException::configInvalid('The sensor display precision must not be negative.');
37        }
38
39        if (($deviceClass === SensorDeviceClass::Enum) !== ($options !== [])) {
40            throw ExposureException::configInvalid('A sensor takes options exactly when its device class is enum.');
41        }
42
43        if (\in_array('', $options, true) || \count(array_unique($options)) !== \count($options)) {
44            throw ExposureException::configInvalid('The sensor options must be distinct and not empty.');
45        }
46    }
47
48    public function getPlatform(): ExposedPlatform
49    {
50        return ExposedPlatform::Sensor;
51    }
52
53    /** @throws ExposureException */
54    public function formatState(int|float|string|DateTimeInterface|null $value): int|float|string|null
55    {
56        return match (true) {
57            $value instanceof DateTimeInterface && $this->deviceClass?->takesDateTime() === true => CalendarStateFormat::formatDateTime($value),
58            $value instanceof DateTimeInterface && $this->deviceClass?->takesDate() === true => CalendarStateFormat::formatDate($value),
59            $value instanceof DateTimeInterface => throw ExposureException::stateInvalid('A date or time needs the timestamp or date device class.'),
60            \is_float($value) && !is_finite($value) => throw ExposureException::stateInvalid('A sensor state must be a finite number.'),
61            default => $value,
62        };
63    }
64}