Completed
Push — master ( 434333...522fec )
by Chris
04:24
created

MessageDecoder::decodeSetWaveformOptional()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 2
dl 0
loc 18
ccs 0
cts 15
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace DaveRandom\LibLifxLan\Decoding;
4
5
use DaveRandom\LibLifxLan\DataTypes as DeviceDataTypes;
6
use DaveRandom\LibLifxLan\DataTypes\Light as LightDataTypes;
7
use DaveRandom\LibLifxLan\Decoding\Exceptions\DecodingException;
8
use DaveRandom\LibLifxLan\Decoding\Exceptions\InvalidMessagePayloadLengthException;
9
use DaveRandom\LibLifxLan\Messages\Device\Commands as DeviceCommands;
10
use DaveRandom\LibLifxLan\Messages\Device\Requests as DeviceRequests;
11
use DaveRandom\LibLifxLan\Messages\Device\Responses as DeviceResponses;
12
use DaveRandom\LibLifxLan\Messages\Light\Commands as LightCommmands;
13
use DaveRandom\LibLifxLan\Messages\Light\Requests as LightRequests;
14
use DaveRandom\LibLifxLan\Messages\Light\Responses as LightResponses;
15
use DaveRandom\LibLifxLan\Messages\Message;
16
use DaveRandom\LibLifxLan\Messages\UnknownMessage;
17
use Ramsey\Uuid\UuidFactory;
18
use Ramsey\Uuid\UuidFactoryInterface;
19
use function DaveRandom\LibLifxLan\nanotime_to_datetimeimmutable;
20
use function DaveRandom\LibLifxLan\uint16_to_int16;
21
22
final class MessageDecoder
23
{
24
    private const HSBK_FORMAT = 'vhue/vsaturation/vbrightness/vtemperature';
25
26
    private const SET_WAVEFORM_FORMAT
27
        = 'Creserved/Ctransient'
28
        . '/' . self::HSBK_FORMAT
29
        . '/Vperiod/' . \DaveRandom\LibLifxLan\FLOAT32_CODE . 'cycles/vskewRatio/Cwaveform'
30
    ;
31
32
    /**
33
     * @uses decodeAcknowledgement
34
     * @uses decodeEchoRequest
35
     * @uses decodeEchoResponse
36
     * @uses decodeGetGroup
37
     * @uses decodeSetGroup
38
     * @uses decodeStateGroup
39
     * @uses decodeGetHostFirmware
40
     * @uses decodeStateHostFirmware
41
     * @uses decodeGetHostInfo
42
     * @uses decodeStateHostInfo
43
     * @uses decodeGetInfo
44
     * @uses decodeStateInfo
45
     * @uses decodeGetLabel
46
     * @uses decodeSetLabel
47
     * @uses decodeStateLabel
48
     * @uses decodeGetLocation
49
     * @uses decodeSetLocation
50
     * @uses decodeStateLocation
51
     * @uses decodeGetDevicePower
52
     * @uses decodeSetDevicePower
53
     * @uses decodeStateDevicePower
54
     * @uses decodeGetService
55
     * @uses decodeStateService
56
     * @uses decodeGetVersion
57
     * @uses decodeStateVersion
58
     * @uses decodeGetWifiFirmware
59
     * @uses decodeStateWifiFirmware
60
     * @uses decodeGetWifiInfo
61
     * @uses decodeStateWifiInfo
62
     * @uses decodeGet
63
     * @uses decodeSetColor
64
     * @uses decodeSetWaveform
65
     * @uses decodeSetWaveformOptional
66
     * @uses decodeState
67
     * @uses decodeGetInfrared
68
     * @uses decodeSetInfrared
69
     * @uses decodeStateInfrared
70
     * @uses decodeGetLightPower
71
     * @uses decodeSetLightPower
72
     * @uses decodeStateLightPower
73
     */
74
    private const MESSAGE_INFO = [
75
        // Device command messages
76
        DeviceCommands\SetGroup::MESSAGE_TYPE_ID => [DeviceCommands\SetGroup::WIRE_SIZE, 'SetGroup'],
77
        DeviceCommands\SetLabel::MESSAGE_TYPE_ID => [DeviceCommands\SetLabel::WIRE_SIZE, 'SetLabel'],
78
        DeviceCommands\SetLocation::MESSAGE_TYPE_ID => [DeviceCommands\SetLocation::WIRE_SIZE, 'SetLocation'],
79
        DeviceCommands\SetPower::MESSAGE_TYPE_ID => [DeviceCommands\SetPower::WIRE_SIZE, 'SetDevicePower'],
80
81
        // Device request messages
82
        DeviceRequests\EchoRequest::MESSAGE_TYPE_ID => [DeviceRequests\EchoRequest::WIRE_SIZE, 'EchoRequest'],
83
        DeviceRequests\GetGroup::MESSAGE_TYPE_ID => [DeviceRequests\GetGroup::WIRE_SIZE, 'GetGroup'],
84
        DeviceRequests\GetHostFirmware::MESSAGE_TYPE_ID => [DeviceRequests\GetHostFirmware::WIRE_SIZE, 'GetHostFirmware'],
85
        DeviceRequests\GetHostInfo::MESSAGE_TYPE_ID => [DeviceRequests\GetHostInfo::WIRE_SIZE, 'GetHostInfo'],
86
        DeviceRequests\GetInfo::MESSAGE_TYPE_ID => [DeviceRequests\GetInfo::WIRE_SIZE, 'GetInfo'],
87
        DeviceRequests\GetLabel::MESSAGE_TYPE_ID => [DeviceRequests\GetLabel::WIRE_SIZE, 'GetLabel'],
88
        DeviceRequests\GetLocation::MESSAGE_TYPE_ID => [DeviceRequests\GetLocation::WIRE_SIZE, 'GetLocation'],
89
        DeviceRequests\GetPower::MESSAGE_TYPE_ID => [DeviceRequests\GetPower::WIRE_SIZE, 'GetDevicePower'],
90
        DeviceRequests\GetService::MESSAGE_TYPE_ID => [DeviceRequests\GetService::WIRE_SIZE, 'GetService'],
91
        DeviceRequests\GetVersion::MESSAGE_TYPE_ID => [DeviceRequests\GetVersion::WIRE_SIZE, 'GetVersion'],
92
        DeviceRequests\GetWifiFirmware::MESSAGE_TYPE_ID => [DeviceRequests\GetWifiFirmware::WIRE_SIZE, 'GetWifiFirmware'],
93
        DeviceRequests\GetWifiInfo::MESSAGE_TYPE_ID => [DeviceRequests\GetWifiInfo::WIRE_SIZE, 'GetWifiInfo'],
94
95
        // Device response messages
96
        DeviceResponses\Acknowledgement::MESSAGE_TYPE_ID => [DeviceResponses\Acknowledgement::WIRE_SIZE, 'Acknowledgement'],
97
        DeviceResponses\EchoResponse::MESSAGE_TYPE_ID => [DeviceResponses\EchoResponse::WIRE_SIZE, 'EchoResponse'],
98
        DeviceResponses\StateGroup::MESSAGE_TYPE_ID => [DeviceResponses\StateGroup::WIRE_SIZE, 'StateGroup'],
99
        DeviceResponses\StateHostFirmware::MESSAGE_TYPE_ID => [DeviceResponses\StateHostFirmware::WIRE_SIZE, 'StateHostFirmware'],
100
        DeviceResponses\StateHostInfo::MESSAGE_TYPE_ID => [DeviceResponses\StateHostInfo::WIRE_SIZE, 'StateHostInfo'],
101
        DeviceResponses\StateInfo::MESSAGE_TYPE_ID => [DeviceResponses\StateInfo::WIRE_SIZE, 'StateInfo'],
102
        DeviceResponses\StateLabel::MESSAGE_TYPE_ID => [DeviceResponses\StateLabel::WIRE_SIZE, 'StateLabel'],
103
        DeviceResponses\StateLocation::MESSAGE_TYPE_ID => [DeviceResponses\StateLocation::WIRE_SIZE, 'StateLocation'],
104
        DeviceResponses\StatePower::MESSAGE_TYPE_ID => [DeviceResponses\StatePower::WIRE_SIZE, 'StateDevicePower'],
105
        DeviceResponses\StateService::MESSAGE_TYPE_ID => [DeviceResponses\StateService::WIRE_SIZE, 'StateService'],
106
        DeviceResponses\StateVersion::MESSAGE_TYPE_ID => [DeviceResponses\StateVersion::WIRE_SIZE, 'StateVersion'],
107
        DeviceResponses\StateWifiFirmware::MESSAGE_TYPE_ID => [DeviceResponses\StateWifiFirmware::WIRE_SIZE, 'StateWifiFirmware'],
108
        DeviceResponses\StateWifiInfo::MESSAGE_TYPE_ID => [DeviceResponses\StateWifiInfo::WIRE_SIZE, 'StateWifiInfo'],
109
110
        // Light command messages
111
        LightCommmands\SetColor::MESSAGE_TYPE_ID => [LightCommmands\SetColor::WIRE_SIZE, 'SetColor'],
112
        LightCommmands\SetInfrared::MESSAGE_TYPE_ID => [LightCommmands\SetInfrared::WIRE_SIZE, 'SetInfrared'],
113
        LightCommmands\SetPower::MESSAGE_TYPE_ID => [LightCommmands\SetPower::WIRE_SIZE, 'SetLightPower'],
114
        LightCommmands\SetWaveform::MESSAGE_TYPE_ID => [LightCommmands\SetWaveform::WIRE_SIZE, 'SetWaveform'],
115
        LightCommmands\SetWaveformOptional::MESSAGE_TYPE_ID => [LightCommmands\SetWaveformOptional::WIRE_SIZE, 'SetWaveformOptional'],
116
117
        // Light request messages
118
        LightRequests\Get::MESSAGE_TYPE_ID => [LightRequests\Get::WIRE_SIZE, 'Get'],
119
        LightRequests\GetInfrared::MESSAGE_TYPE_ID => [LightRequests\GetInfrared::WIRE_SIZE, 'GetInfrared'],
120
        LightRequests\GetPower::MESSAGE_TYPE_ID => [LightRequests\GetPower::WIRE_SIZE, 'GetLightPower'],
121
122
        // Light response messages
123
        LightResponses\State::MESSAGE_TYPE_ID => [LightResponses\State::WIRE_SIZE, 'State'],
124
        LightResponses\StateInfrared::MESSAGE_TYPE_ID => [LightResponses\StateInfrared::WIRE_SIZE, 'StateInfrared'],
125
        LightResponses\StatePower::MESSAGE_TYPE_ID => [LightResponses\StatePower::WIRE_SIZE, 'StateLightPower'],
126
    ];
127
128
    private $uuidFactory;
129
130
    private function createBitField(array $map): int
131
    {
132
        $result = 0;
133
134
        foreach ($map as $flag => $enabled) {
135
            if ($enabled) {
136
                $result |= $flag;
137
            }
138
        }
139
140
        return $result;
141
    }
142
143
    private function decodeAcknowledgement(): DeviceResponses\Acknowledgement
144
    {
145
        return new DeviceResponses\Acknowledgement();
146
    }
147
148
    private function decodeEchoRequest(string $data, int $offset): DeviceRequests\EchoRequest
149
    {
150
        return new DeviceRequests\EchoRequest(\substr($data, $offset));
151
    }
152
153
    private function decodeEchoResponse(string $data, int $offset): DeviceResponses\EchoResponse
154
    {
155
        return new DeviceResponses\EchoResponse(\substr($data, $offset));
156
    }
157
158
    private function decodeGetGroup(): DeviceRequests\GetGroup
159
    {
160
        return new DeviceRequests\GetGroup;
161
    }
162
163
    private function decodeSetGroup(string $data, int $offset): DeviceCommands\SetGroup
164
    {
165
        [
166
            'guid'    => $guid,
167
            'label'   => $label,
168
            'updated' => $updatedAt,
169
        ] = \unpack('a16guid/a32label/Pupdated', $data, $offset);
170
171
        $guid = $this->uuidFactory->fromBytes($guid);
172
        $updatedAt = nanotime_to_datetimeimmutable($updatedAt);
173
        $label = new DeviceDataTypes\Label(\rtrim($label, "\x00"));
174
175
        return new DeviceCommands\SetGroup(new DeviceDataTypes\Group($guid, $label, $updatedAt));
176
    }
177
178
    private function decodeStateGroup(string $data, int $offset): DeviceResponses\StateGroup
179
    {
180
        [
181
            'guid'    => $guid,
182
            'label'   => $label,
183
            'updated' => $updatedAt,
184
        ] = \unpack('a16guid/a32label/Pupdated', $data, $offset);
185
186
        $guid = $this->uuidFactory->fromBytes($guid);
187
        $updatedAt = nanotime_to_datetimeimmutable($updatedAt);
188
        $label = new DeviceDataTypes\Label(\rtrim($label, "\x00"));
189
190
        return new DeviceResponses\StateGroup(new DeviceDataTypes\Group($guid, $label, $updatedAt));
191
    }
192
193
    private function decodeGetHostFirmware(): DeviceRequests\GetHostFirmware
194
    {
195
        return new DeviceRequests\GetHostFirmware;
196
    }
197
198
    private function decodeStateHostFirmware(string $data, int $offset): DeviceResponses\StateHostFirmware
199
    {
200
        [
201
            'build'    => $build,
202
            'version'  => $version,
203
        ] = \unpack('Pbuild/Preserved/Vversion', $data, $offset);
204
205
        $build = nanotime_to_datetimeimmutable($build);
206
207
        return new DeviceResponses\StateHostFirmware(new DeviceDataTypes\HostFirmware($build, $version));
208
    }
209
210
    private function decodeGetHostInfo(): DeviceRequests\GetHostInfo
211
    {
212
        return new DeviceRequests\GetHostInfo;
213
    }
214
215
    private function decodeStateHostInfo(string $data, int $offset): DeviceResponses\StateHostInfo
216
    {
217
        [
218
            'signal'   => $signal,
219
            'tx'       => $tx,
220
            'rx'       => $rx,
221
        ] = \unpack(\DaveRandom\LibLifxLan\FLOAT32_CODE . 'signal/Vtx/Vrx/vreserved', $data, $offset);
222
223
        return new DeviceResponses\StateHostInfo(new DeviceDataTypes\HostInfo($signal, $tx, $rx));
224
    }
225
226
    private function decodeGetInfo(): DeviceRequests\GetInfo
227
    {
228
        return new DeviceRequests\GetInfo;
229
    }
230
231
    private function decodeStateInfo(string $data, int $offset): DeviceResponses\StateInfo
232
    {
233
        [
234
            'time'     => $time,
235
            'uptime'   => $uptime,
236
            'downtime' => $downtime,
237
        ] = \unpack('Ptime/Puptime/Pdowntime', $data, $offset);
238
239
        $time = nanotime_to_datetimeimmutable($time);
240
241
        return new DeviceResponses\StateInfo(new DeviceDataTypes\TimeInfo($time, $uptime, $downtime));
242
    }
243
244
    private function decodeGetLabel(): DeviceRequests\GetLabel
245
    {
246
        return new DeviceRequests\GetLabel;
247
    }
248
249
    private function decodeSetLabel(string $data, int $offset): DeviceCommands\SetLabel
250
    {
251
        return new DeviceCommands\SetLabel(new DeviceDataTypes\Label(\rtrim(\substr($data, $offset), "\x00")));
252
    }
253
254
    private function decodeStateLabel(string $data, int $offset): DeviceResponses\StateLabel
255
    {
256
        return new DeviceResponses\StateLabel(new DeviceDataTypes\Label(\rtrim(\substr($data, $offset), "\x00")));
257
    }
258
259
    private function decodeGetLocation(): DeviceRequests\GetLocation
260
    {
261
        return new DeviceRequests\GetLocation;
262
    }
263
264
    private function decodeSetLocation(string $data, int $offset): DeviceCommands\SetLocation
265
    {
266
        [
267
            'guid'    => $guid,
268
            'label'   => $label,
269
            'updated' => $updatedAt,
270
        ] = \unpack('a16guid/a32label/Pupdated', $data, $offset);
271
272
        $guid = $this->uuidFactory->fromBytes($guid);
273
        $updatedAt = nanotime_to_datetimeimmutable($updatedAt);
274
        $label = new DeviceDataTypes\Label(\rtrim($label, "\x00"));
275
276
        return new DeviceCommands\SetLocation(new DeviceDataTypes\Location($guid, $label, $updatedAt));
277
    }
278
279
    private function decodeStateLocation(string $data, int $offset): DeviceResponses\StateLocation
280
    {
281
        [
282
            'guid'    => $guid,
283
            'label'   => $label,
284
            'updated' => $updatedAt,
285
        ] = \unpack('a16guid/a32label/Pupdated', $data, $offset);
286
287
        $guid = $this->uuidFactory->fromBytes($guid);
288
        $updatedAt = nanotime_to_datetimeimmutable($updatedAt);
289
        $label = new DeviceDataTypes\Label(\rtrim($label, "\x00"));
290
291
        return new DeviceResponses\StateLocation(new DeviceDataTypes\Location($guid, $label, $updatedAt));
292
    }
293
294
    private function decodeGetDevicePower(): DeviceRequests\GetPower
295
    {
296
        return new DeviceRequests\GetPower;
297
    }
298
299
    private function decodeSetDevicePower(string $data, int $offset): DeviceCommands\SetPower
300
    {
301
        $level = \unpack('vlevel', $data, $offset)['level'];
302
303
        return new DeviceCommands\SetPower($level);
304
    }
305
306
    private function decodeStateDevicePower(string $data, int $offset): DeviceResponses\StatePower
307
    {
308
        $level = \unpack('vlevel', $data, $offset)['level'];
309
310
        return new DeviceResponses\StatePower($level);
311
    }
312
313
    private function decodeGetService(): DeviceRequests\GetService
314
    {
315
        return new DeviceRequests\GetService;
316
    }
317
318
    private function decodeStateService(string $data, int $offset): DeviceResponses\StateService
319
    {
320
        [
321
            'serviceType' => $serviceType,
322
            'port' => $port,
323
        ] = \unpack('CserviceType/Vport', $data, $offset);
324
325
        return new DeviceResponses\StateService(new DeviceDataTypes\Service($serviceType, $port));
326
    }
327
328
    private function decodeGetVersion(): DeviceRequests\GetVersion
329
    {
330
        return new DeviceRequests\GetVersion;
331
    }
332
333
    private function decodeStateVersion(string $data, int $offset): DeviceResponses\StateVersion
334
    {
335
        [
336
            'vendor'  => $vendor,
337
            'product' => $product,
338
            'version' => $version,
339
        ] = \unpack('Vvendor/Vproduct/Vversion', $data, $offset);
340
341
        return new DeviceResponses\StateVersion(new DeviceDataTypes\Version($vendor, $product, $version));
342
    }
343
344
    private function decodeGetWifiFirmware(): DeviceRequests\GetWifiFirmware
345
    {
346
        return new DeviceRequests\GetWifiFirmware;
347
    }
348
349
    private function decodeStateWifiFirmware(string $data, int $offset): DeviceResponses\StateWifiFirmware
350
    {
351
        [
352
            'build'    => $build,
353
            'version'  => $version,
354
        ] = \unpack('Pbuild/Preserved/Vversion', $data, $offset);
355
356
        $build = nanotime_to_datetimeimmutable($build);
357
358
        return new DeviceResponses\StateWifiFirmware(new DeviceDataTypes\WifiFirmware($build, $version));
359
    }
360
361
    private function decodeGetWifiInfo(): DeviceRequests\GetWifiInfo
362
    {
363
        return new DeviceRequests\GetWifiInfo;
364
    }
365
366
    private function decodeStateWifiInfo(string $data, int $offset): DeviceResponses\StateWifiInfo
367
    {
368
369
        [
370
            'signal'   => $signal,
371
            'tx'       => $tx,
372
            'rx'       => $rx,
373
        ] = \unpack(\DaveRandom\LibLifxLan\FLOAT32_CODE . 'signal/Vtx/Vrx/vreserved', $data, $offset);
374
375
        return new DeviceResponses\StateWifiInfo(new DeviceDataTypes\WifiInfo($signal, $tx, $rx));
376
    }
377
378
    private function decodeGet(): LightRequests\Get
379
    {
380
        return new LightRequests\Get;
381
    }
382
383 4
    private function decodeSetColor(string $data, int $offset): LightCommmands\SetColor
384
    {
385
        [
386 4
            'hue'         => $hue,
387 4
            'saturation'  => $saturation,
388 4
            'brightness'  => $brightness,
389 4
            'temperature' => $temperature,
390 4
            'duration'    => $duration,
391 4
        ] = \unpack('Creserved/' . self::HSBK_FORMAT . '/Vduration', $data, $offset);
392
393 4
        $color = new LightDataTypes\HsbkColor($hue, $saturation, $brightness, $temperature);
394
395 4
        return new LightCommmands\SetColor(new LightDataTypes\ColorTransition($color, $duration));
396
    }
397
398
    private function decodeSetWaveform(string $data, int $offset): LightCommmands\SetWaveform
399
    {
400
        [
401
            'transient' => $transient,
402
            'hue' => $hue, 'saturation' => $saturation, 'brightness' => $brightness, 'temperature' => $temperature,
403
            'period' => $period, 'cycles' => $cycles, 'skewRatio' => $skewRatio, 'waveform' => $waveform,
404
        ] = \unpack(self::SET_WAVEFORM_FORMAT, $data, $offset);
405
406
        return new LightCommmands\SetWaveform(new LightDataTypes\Effect(
407
            (bool)$transient,
408
            new LightDataTypes\HsbkColor($hue, $saturation, $brightness, $temperature),
409
            $period, $cycles, uint16_to_int16($skewRatio), $waveform
410
        ));
411
    }
412
413
    private function decodeSetWaveformOptional(string $data, int $offset): LightCommmands\SetWaveformOptional
414
    {
415
        [
416
            'transient' => $transient,
417
            'hue' => $hue, 'saturation' => $saturation, 'brightness' => $brightness, 'temperature' => $temperature,
418
            'period' => $period, 'cycles' => $cycles, 'skewRatio' => $skewRatio, 'waveform' => $waveform,
419
            'setH' => $setHue, 'setS' => $setSaturation, 'setB' => $setBrightness, 'setK' => $setTemperature,
420
        ] = \unpack(self::SET_WAVEFORM_FORMAT . '/CsetH/CsetS/CsetB/CsetK', $data, $offset);
421
422
        return new LightCommmands\SetWaveformOptional(new LightDataTypes\Effect(
423
            (bool)$transient,
424
            new LightDataTypes\HsbkColor($hue, $saturation, $brightness, $temperature),
425
            $period, $cycles, uint16_to_int16($skewRatio), $waveform,
426
            $this->createBitField([
427
                LightDataTypes\Effect::SET_HUE => $setHue,
428
                LightDataTypes\Effect::SET_SATURATION => $setSaturation,
429
                LightDataTypes\Effect::SET_BRIGHTNESS => $setBrightness,
430
                LightDataTypes\Effect::SET_TEMPERATURE => $setTemperature,
431
            ])
432
        ));
433
    }
434
435
    private function decodeState(string $data, int $offset): LightResponses\State
436
    {
437
        [
438
            'hue'         => $hue,
439
            'saturation'  => $saturation,
440
            'brightness'  => $brightness,
441
            'temperature' => $temperature,
442
            'power'       => $power,
443
            'label'       => $label,
444
        ] = \unpack(self::HSBK_FORMAT . '/vreserved/vpower/a32label/Preserved', $data, $offset);
445
446
        $color = new LightDataTypes\HsbkColor($hue, $saturation, $brightness, $temperature);
447
        $label = new DeviceDataTypes\Label(\rtrim($label, "\x00"));
448
449
        return new LightResponses\State(new LightDataTypes\State($color, $power, $label));
450
    }
451
452
    private function decodeGetInfrared(): LightRequests\GetInfrared
453
    {
454
        return new LightRequests\GetInfrared;
455
    }
456
457
    private function decodeSetInfrared(string $data, int $offset): LightCommmands\SetInfrared
458
    {
459
        $level = \unpack('vlevel', $data, $offset)['level'];
460
461
        return new LightCommmands\SetInfrared($level);
462
    }
463
464
    private function decodeStateInfrared(string $data, int $offset): LightResponses\StateInfrared
465
    {
466
        $level = \unpack('vlevel', $data, $offset)['level'];
467
468
        return new LightResponses\StateInfrared($level);
469
    }
470
471
    private function decodeGetLightPower(): LightRequests\GetPower
472
    {
473
        return new LightRequests\GetPower;
474
    }
475
476
    private function decodeSetLightPower(string $data, int $offset): LightCommmands\SetPower
477
    {
478
        [
479
            'level'    => $level,
480
            'duration' => $duration,
481
        ] = \unpack('vlevel/Vduration', $data, $offset);
482
483
        return new LightCommmands\SetPower(new LightDataTypes\PowerTransition($level, $duration));
484
    }
485
486
    private function decodeStateLightPower(string $data, int $offset): LightResponses\StatePower
487
    {
488
        $level = \unpack('vlevel', $data, $offset)['level'];
489
490
        return new LightResponses\StatePower($level);
491
    }
492
493 16
    public function __construct(UuidFactoryInterface $uuidFactory = null)
494
    {
495 16
        $this->uuidFactory = $uuidFactory ?? new UuidFactory;
496
    }
497
498
    /**
499
     * @param int $type
500
     * @param string $data
501
     * @param int $offset
502
     * @param int|null $dataLength
503
     * @return Message
504
     * @throws DecodingException
505
     */
506 4
    public function decodeMessage(int $type, string $data, int $offset = 0, int $dataLength = null): Message
507
    {
508 4
        $dataLength = $dataLength ?? (\strlen($data) - $offset);
509
510 4
        if (!\array_key_exists($type, self::MESSAGE_INFO)) {
511
            return new UnknownMessage($type, \substr($data, $offset, $dataLength));
512
        }
513
514 4
        [$expectedLength, $messageName] = self::MESSAGE_INFO[$type];
515
516 4
        if ($dataLength !== $expectedLength) {
517
            throw new InvalidMessagePayloadLengthException(
518
                "Invalid payload length for {$messageName} message,"
519
                . " expecting {$expectedLength} bytes, got {$dataLength} bytes"
520
            );
521
        }
522
523 4
        return ([$this, 'decode' . $messageName])($data, $offset);
524
    }
525
}
526