Completed
Push — master ( 23013c...52630b )
by Chris
04:20
created

MessageEncoder::dateTimeToNanoseconds()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
ccs 0
cts 7
cp 0
crap 6
rs 9.6666
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A MessageEncoder::encodeUnknownMessage() 0 3 1
1
<?php declare(strict_types=1);
2
3
namespace DaveRandom\LibLifxLan\Encoding;
4
5
use DaveRandom\LibLifxLan\DataTypes as DeviceDataTypes;
6
use DaveRandom\LibLifxLan\DataTypes\Light as LightDataTypes;
7
use function DaveRandom\LibLifxLan\datetimeinterface_to_nanotime;
1 ignored issue
show
introduced by
The function DaveRandom\LibLifxLan\da...meinterface_to_nanotime was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
8
use DaveRandom\LibLifxLan\Encoding\Exceptions\InvalidMessageException;
9
use DaveRandom\LibLifxLan\Exceptions\InvalidValueException;
10
use function DaveRandom\LibLifxLan\int16_to_uint16;
1 ignored issue
show
introduced by
The function DaveRandom\LibLifxLan\int16_to_uint16 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
11
use DaveRandom\LibLifxLan\Messages\Device\Commands as DeviceCommands;
12
use DaveRandom\LibLifxLan\Messages\Device\Requests as DeviceRequests;
13
use DaveRandom\LibLifxLan\Messages\Device\Responses as DeviceResponses;
14
use DaveRandom\LibLifxLan\Messages\Light\Commands as LightCommands;
15
use DaveRandom\LibLifxLan\Messages\Light\Responses as LightResponses;
16
use DaveRandom\LibLifxLan\Messages\Message;
17
use DaveRandom\LibLifxLan\Messages\UnknownMessage;
18
use const DaveRandom\LibLifxLan\FLOAT32_CODE;
19
20
final class MessageEncoder
21
{
22
    /**
23
     * @uses encodeUnknownMessage
24
     * @uses encodeEchoRequest
25
     * @uses encodeEchoResponse
26
     * @uses encodeSetGroup
27
     * @uses encodeStateGroup
28
     * @uses encodeStateHostFirmware
29
     * @uses encodeStateHostInfo
30
     * @uses encodeStateInfo
31
     * @uses encodeSetLabel
32
     * @uses encodeStateLabel
33
     * @uses encodeSetLocation
34
     * @uses encodeStateLocation
35
     * @uses encodeSetDevicePower
36
     * @uses encodeStateDevicePower
37
     * @uses encodeStateService
38
     * @uses encodeStateVersion
39
     * @uses encodeStateWifiFirmware
40
     * @uses encodeStateWifiInfo
41
     * @uses encodeSetColor
42
     * @uses encodeSetWaveform
43
     * @uses encodeSetWaveformOptional
44
     * @uses encodeState
45
     * @uses encodeSetInfrared
46
     * @uses encodeStateInfrared
47
     * @uses encodeSetLightPower
48
     * @uses encodeStateLightPower
49
     */
50
    private const ENCODING_ROUTINES = [
51
        UnknownMessage::class => 'UnknownMessage',
52
53
        // Device command messages
54
        DeviceCommands\SetGroup::class => 'SetGroup',
55
        DeviceCommands\SetLabel::class => 'SetLabel',
56
        DeviceCommands\SetLocation::class => 'SetLocation',
57
        DeviceCommands\SetPower::class => 'SetDevicePower',
58
59
        // Device request messages
60
        DeviceRequests\EchoRequest::class => 'EchoRequest',
61
62
        // Device response messages
63
        DeviceResponses\EchoResponse::class => 'EchoResponse',
64
        DeviceResponses\StateGroup::class => 'StateGroup',
65
        DeviceResponses\StateHostFirmware::class => 'StateHostFirmware',
66
        DeviceResponses\StateHostInfo::class => 'StateHostInfo',
67
        DeviceResponses\StateInfo::class => 'StateInfo',
68
        DeviceResponses\StateLabel::class => 'StateLabel',
69
        DeviceResponses\StateLocation::class => 'StateLocation',
70
        DeviceResponses\StatePower::class => 'StateDevicePower',
71
        DeviceResponses\StateService::class => 'StateService',
72
        DeviceResponses\StateVersion::class => 'StateVersion',
73
        DeviceResponses\StateWifiFirmware::class => 'StateWifiFirmware',
74
        DeviceResponses\StateWifiInfo::class => 'StateWifiInfo',
75
76
        // Light command messages
77
        LightCommands\SetColor::class => 'SetColor',
78
        LightCommands\SetInfrared::class => 'SetInfrared',
79
        LightCommands\SetPower::class => 'SetLightPower',
80
        LightCommands\SetWaveform::class => 'SetWaveform',
81
        LightCommands\SetWaveformOptional::class => 'SetWaveformOptional',
82
83
        // Light response messages
84
        LightResponses\State::class => 'State',
85
        LightResponses\StateInfrared::class => 'StateInfrared',
86
        LightResponses\StatePower::class => 'StateLightPower',
87
    ];
88
89
    private function encodeHsbkColor(LightDataTypes\HsbkColor $color): string
90
    {
91
        return \pack('v4', $color->getHue(), $color->getSaturation(), $color->getBrightness(), $color->getTemperature());
92
    }
93
94
    /**
95
     * @param DeviceDataTypes\Location $location
96
     * @return string
97
     * @throws InvalidMessageException
98
     */
99
    private function encodeLocation(DeviceDataTypes\Location $location): string
100
    {
101
        try {
102
            return \pack(
103
                'a16a32P',
104
                $location->getGuid()->getBytes(),
105
                $location->getLabel()->getValue(),
106
                datetimeinterface_to_nanotime($location->getUpdatedAt())
107
            );
108
        } catch (InvalidValueException $e) {
109
            throw new InvalidMessageException($e->getMessage(), $e->getCode(), $e);
110
        }
111
    }
112
113
    /**
114
     * @param DeviceDataTypes\Group $group
115
     * @return string
116
     * @throws InvalidMessageException
117
     */
118
    private function encodeGroup(DeviceDataTypes\Group $group): string
119
    {
120
        try {
121
            return \pack(
122
                'a16a32P',
123
                $group->getGuid()->getBytes(),
124
                $group->getLabel()->getValue(),
125
                datetimeinterface_to_nanotime($group->getUpdatedAt())
126
            );
127
        } catch (InvalidValueException $e) {
128
            throw new InvalidMessageException($e->getMessage(), $e->getCode(), $e);
129
        }
130
    }
131
132
    /**
133
     * @param DeviceDataTypes\Firmware $firmware
134
     * @return string
135
     * @throws InvalidMessageException
136
     */
137
    private function encodeFirmware(DeviceDataTypes\Firmware $firmware)
138
    {
139
        try {
140
            return \pack(
141
                'PPV',
142
                datetimeinterface_to_nanotime($firmware->getBuild()),
143
                0, // reserved
144
                $firmware->getVersion()
145
            );
146
        } catch (InvalidValueException $e) {
147
            throw new InvalidMessageException($e->getMessage(), $e->getCode(), $e);
148
        }
149
    }
150
151
    private function encodeNetworkInfo(DeviceDataTypes\NetworkInfo $info): string
152
    {
153
        return \pack(
154
            FLOAT32_CODE . 'VVv',
155
            $info->getSignal(),
156
            $info->getTx(),
157
            $info->getRx(),
158
            0 // reserved
159
        );
160
    }
161
162
    private function encodeUnknownMessage(UnknownMessage $message): string
163
    {
164
        return $message->getData();
165
    }
166
167
    private function encodeStateVersion(DeviceResponses\StateVersion $message): string
168
    {
169
        $version = $message->getVersion();
170
171
        return \pack('VVV', $version->getVendor(), $version->getProduct(), $version->getVersion());
172
    }
173
174
    private function encodeStateService(DeviceResponses\StateService $message): string
175
    {
176
        $service = $message->getService();
177
178
        return \pack('CV', $service->getTypeId(), $service->getPort());
179
    }
180
181
    /**
182
     * @param DeviceResponses\StateInfo $message
183
     * @return string
184
     * @throws InvalidMessageException
185
     */
186
    private function encodeStateInfo(DeviceResponses\StateInfo $message): string
187
    {
188
        $info = $message->getInfo();
189
190
        try {
191
            return \pack('PPP', datetimeinterface_to_nanotime($info->getTime()), $info->getUptime(), $info->getDowntime());
192
        } catch (InvalidValueException $e) {
193
            throw new InvalidMessageException($e->getMessage(), $e->getCode(), $e);
194
        }
195
    }
196
197
    /**
198
     * @param DeviceResponses\StateHostFirmware $message
199
     * @return string
200
     * @throws InvalidMessageException
201
     */
202
    private function encodeStateHostFirmware(DeviceResponses\StateHostFirmware $message): string
203
    {
204
        return $this->encodeFirmware($message->getHostFirmware());
205
    }
206
207
    private function encodeStateHostInfo(DeviceResponses\StateHostInfo $message): string
208
    {
209
        return $this->encodeNetworkInfo($message->getHostInfo());
210
    }
211
212
    /**
213
     * @param DeviceResponses\StateWifiFirmware $message
214
     * @return string
215
     * @throws InvalidMessageException
216
     */
217
    private function encodeStateWifiFirmware(DeviceResponses\StateWifiFirmware $message): string
218
    {
219
        return $this->encodeFirmware($message->getWifiFirmware());
220
    }
221
222
    private function encodeStateWifiInfo(DeviceResponses\StateWifiInfo $message): string
223
    {
224
        return $this->encodeNetworkInfo($message->getWifiInfo());
225
    }
226
227
    /**
228
     * @param DeviceCommands\SetGroup $message
229
     * @return string
230
     * @throws InvalidMessageException
231
     */
232
    private function encodeSetGroup(DeviceCommands\SetGroup $message): string
233
    {
234
        return $this->encodeGroup($message->getGroup());
235
    }
236
237
    /**
238
     * @param DeviceResponses\StateGroup $message
239
     * @return string
240
     * @throws InvalidMessageException
241
     */
242
    private function encodeStateGroup(DeviceResponses\StateGroup $message): string
243
    {
244
        return $this->encodeGroup($message->getGroup());
245
    }
246
247
    /**
248
     * @param DeviceCommands\SetLabel $message
249
     * @return string
250
     */
251
    private function encodeSetLabel(DeviceCommands\SetLabel $message): string
252
    {
253
        return \pack('a32', $message->getLabel()->getValue());
254
    }
255
256
    /**
257
     * @param DeviceResponses\StateLabel $message
258
     * @return string
259
     */
260
    private function encodeStateLabel(DeviceResponses\StateLabel $message): string
261
    {
262
        return \pack('a32', $message->getLabel()->getValue());
263
    }
264
265
    /**
266
     * @param DeviceCommands\SetLocation $message
267
     * @return string
268
     * @throws InvalidMessageException
269
     */
270
    private function encodeSetLocation(DeviceCommands\SetLocation $message): string
271
    {
272
        return $this->encodeLocation($message->getLocation());
273
    }
274
275
    /**
276
     * @param DeviceResponses\StateLocation $message
277
     * @return string
278
     * @throws InvalidMessageException
279
     */
280
    private function encodeStateLocation(DeviceResponses\StateLocation $message): string
281
    {
282
        return $this->encodeLocation($message->getLocation());
283
    }
284
285
    /**
286
     * @param DeviceCommands\SetPower $message
287
     * @return string
288
     */
289
    private function encodeSetDevicePower(DeviceCommands\SetPower $message): string
290
    {
291
        return \pack('v', $message->getLevel());
292
    }
293
294
    /**
295
     * @param DeviceResponses\StatePower $message
296
     * @return string
297
     */
298
    private function encodeStateDevicePower(DeviceResponses\StatePower $message): string
299
    {
300
        return \pack('v', $message->getLevel());
301
    }
302
303
    /**
304
     * @param DeviceRequests\EchoRequest $message
305
     * @return string
306
     */
307
    private function encodeEchoRequest(DeviceRequests\EchoRequest $message): string
308
    {
309
        return $message->getPayload();
310
    }
311
312
    /**
313
     * @param DeviceResponses\EchoResponse $message
314
     * @return string
315
     */
316
    private function encodeEchoResponse(DeviceResponses\EchoResponse $message): string
317
    {
318
        return $message->getPayload();
319
    }
320
321
    /**
322
     * @param LightCommands\SetColor $message
323
     * @return string
324
     */
325
    private function encodeSetColor(LightCommands\SetColor $message): string
326
    {
327
        $transition = $message->getColorTransition();
328
329
        return "\x00" . $this->encodeHsbkColor($transition->getColor()) . \pack('V', $transition->getDuration());
330
    }
331
332
    /**
333
     * @param LightCommands\SetInfrared $message
334
     * @return string
335
     */
336
    private function encodeSetInfrared(LightCommands\SetInfrared $message): string
337
    {
338
        return \pack('v', $message->getBrightness());
339
    }
340
341
    /**
342
     * @param LightResponses\StateInfrared $message
343
     * @return string
344
     */
345
    private function encodeStateInfrared(LightResponses\StateInfrared $message): string
346
    {
347
        return \pack('v', $message->getBrightness());
348
    }
349
350
    /**
351
     * @param LightCommands\SetPower $message
352
     * @return string
353
     */
354
    private function encodeSetLightPower(LightCommands\SetPower $message): string
355
    {
356
        $transition = $message->getPowerTransition();
357
358
        return \pack('vV', $transition->getLevel(), $transition->getDuration());
359
    }
360
361
    /**
362
     * @param LightResponses\StatePower $message
363
     * @return string
364
     */
365
    private function encodeStateLightPower(LightResponses\StatePower $message): string
366
    {
367
        return \pack('v', $message->getLevel());
368
    }
369
370
    /**
371
     * @param LightResponses\State $message
372
     * @return string
373
     */
374
    private function encodeState(LightResponses\State $message): string
375
    {
376
        $state = $message->getState();
377
378
        return $this->encodeHsbkColor($state->getColor()) . \pack(
379
            'v2a32P',
380
            0, // reserved
381
            $state->getPower(),
382
            $state->getLabel()->getValue(),
383
            0  // reserved
384
        );
385
    }
386
387
    /**
388
     * @param LightCommands\SetWaveform $message
389
     * @return string
390
     */
391
    private function encodeSetWaveform(LightCommands\SetWaveform $message): string
392
    {
393
        $effect = $message->getEffect();
394
        $skew = int16_to_uint16($effect->getSkewRatio());
395
396
        return "\x00" . \chr((int)$effect->isTransient())
397
            . $this->encodeHsbkColor($effect->getColor())
398
            . \pack('V' . FLOAT32_CODE . 'vC', $effect->getPeriod(), $effect->getCycles(), $skew, $effect->getWaveform())
399
        ;
400
    }
401
402
    /**
403
     * @param LightCommands\SetWaveformOptional $message
404
     * @return string
405
     */
406
    private function encodeSetWaveformOptional(LightCommands\SetWaveformOptional $message): string
407
    {
408
        $effect = $message->getEffect();
409
        $skew = int16_to_uint16($effect->getSkewRatio());
410
411
        $options = $effect->getOptions();
412
        $optionData = \pack(
413
            'C4',
414
            (int)(bool)($options & LightDataTypes\Effect::SET_HUE),
415
            (int)(bool)($options & LightDataTypes\Effect::SET_SATURATION),
416
            (int)(bool)($options & LightDataTypes\Effect::SET_BRIGHTNESS),
417
            (int)(bool)($options & LightDataTypes\Effect::SET_TEMPERATURE)
418
        );
419
420
        return "\x00" . \chr((int)$effect->isTransient())
421
            . $this->encodeHsbkColor($effect->getColor())
422
            . \pack('V' . FLOAT32_CODE . 'vC', $effect->getPeriod(), $effect->getCycles(), $skew, $effect->getWaveform())
423
            . $optionData
424
        ;
425
    }
426
427
    public function encodeMessage(Message $message): string
428
    {
429
        return \array_key_exists($class = \get_class($message), self::ENCODING_ROUTINES)
430
            ? $this->{'encode' . self::ENCODING_ROUTINES[$class]}($message)
431
            : '';
432
    }
433
}
434