PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / admin / helpers / src / dooraccess / integration / device.php

device.php in VikBooking Hotel Booking Engine & PMS trunk, at admin/helpers/src/dooraccess/integration/device.php

679 lines 17.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2025 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Door Access integration device decorator. Such objects are
16 * serialized and stored onto the database as blobs.
17 *
18 * @since 1.18.4 (J) - 1.8.4 (WP)
19 */
20 final class VBODooraccessIntegrationDevice
21 {
22 /**
23 * @var array
24 */
25 protected array $payload = [];
26
27 /**
28 * @var array
29 */
30 protected array $connectedListings = [];
31
32 /**
33 * @var array
34 *
35 * @since 1.18.7 (J) - 1.8.7 (WP)
36 */
37 protected array $connectedSubunits = [];
38
39 /**
40 * @var array
41 */
42 protected array $capabilities = [];
43
44 /**
45 * @var ?string
46 */
47 protected ?string $identifier = null;
48
49 /**
50 * @var ?string
51 */
52 protected ?string $name = null;
53
54 /**
55 * @var ?string
56 */
57 protected ?string $description = null;
58
59 /**
60 * @var ?string
61 */
62 protected ?string $icon = null;
63
64 /**
65 * @var ?string
66 */
67 protected ?string $model = null;
68
69 /**
70 * @var ?float
71 */
72 protected ?float $batterylevel = null;
73
74 /**
75 * @var bool
76 */
77 protected bool $dataChanged = false;
78
79 /**
80 * Class constructor.
81 *
82 * @param array $payload The remote device raw payload.
83 */
84 public function __construct(array $payload)
85 {
86 // set device full payload
87 $this->setPayload($payload);
88 }
89
90 /**
91 * Gets the device identification value.
92 *
93 * @return ?string The device identification value.
94 */
95 public function getID()
96 {
97 return $this->identifier;
98 }
99
100 /**
101 * Sets the device identification value.
102 *
103 * @param string $id The identification value.
104 * @param bool $raw True to accept any value.
105 *
106 * @return self
107 *
108 * @since 1.18.7 (J) - 1.8.7 (WP) added argument $raw to support MAC addresses.
109 */
110 public function setID(string $id, bool $raw = false)
111 {
112 if (!$raw) {
113 // sanitize value
114 $id = preg_replace('/[^a-z0-9\-\_\.\|]/i', '', $id);
115 }
116
117 $this->identifier = $id;
118
119 return $this;
120 }
121
122 /**
123 * Gets the device name.
124 *
125 * @return ?string The device name.
126 */
127 public function getName()
128 {
129 return $this->name;
130 }
131
132 /**
133 * Sets the device name.
134 *
135 * @param string $name The device name.
136 *
137 * @return self
138 */
139 public function setName(string $name)
140 {
141 // set value
142 $this->name = $name;
143
144 // turn data-changed flag on
145 $this->setDataChanged(true);
146
147 return $this;
148 }
149
150 /**
151 * Gets the device description.
152 *
153 * @return ?string The device description.
154 */
155 public function getDescription()
156 {
157 return $this->description;
158 }
159
160 /**
161 * Sets the device description.
162 *
163 * @param string $description The device description.
164 *
165 * @return self
166 */
167 public function setDescription(string $description)
168 {
169 // set value
170 $this->description = $description;
171
172 // turn data-changed flag on
173 $this->setDataChanged(true);
174
175 return $this;
176 }
177
178 /**
179 * Gets the device icon (image URI or HTML icon).
180 *
181 * @return ?string The device icon.
182 */
183 public function getIcon()
184 {
185 return $this->icon;
186 }
187
188 /**
189 * Sets the device icon (image URI or HTML icon).
190 *
191 * @param string $icon The device icon.
192 *
193 * @return self
194 */
195 public function setIcon(string $icon)
196 {
197 $this->icon = $icon;
198
199 return $this;
200 }
201
202 /**
203 * Gets the device model.
204 *
205 * @return ?string The device model.
206 */
207 public function getModel()
208 {
209 return $this->model;
210 }
211
212 /**
213 * Sets the device model.
214 *
215 * @param string $model The device model.
216 *
217 * @return self
218 */
219 public function setModel(string $model)
220 {
221 // set value
222 $this->model = $model;
223
224 // turn data-changed flag on
225 $this->setDataChanged(true);
226
227 return $this;
228 }
229
230 /**
231 * Gets the device battery level.
232 *
233 * @return ?float The device battery level.
234 */
235 public function getBatteryLevel()
236 {
237 return $this->batterylevel;
238 }
239
240 /**
241 * Sets the device battery level.
242 *
243 * @param float $level The device battery level.
244 *
245 * @return self
246 */
247 public function setBatteryLevel(float $level)
248 {
249 // set value
250 $this->batterylevel = $level;
251
252 // turn data-changed flag on
253 $this->setDataChanged(true);
254
255 return $this;
256 }
257
258 /**
259 * Gets the device payload.
260 *
261 * @return array The device raw payload.
262 */
263 public function getPayload()
264 {
265 return $this->payload;
266 }
267
268 /**
269 * Sets the device payload.
270 *
271 * @param array $payload The device raw payload.
272 *
273 * @return self
274 */
275 public function setPayload(array $payload)
276 {
277 $this->payload = $payload;
278
279 return $this;
280 }
281
282 /**
283 * Whether data has changed on the device.
284 *
285 * @return bool
286 *
287 * @since 1.18.7 (J) - 1.8.7 (WP)
288 */
289 public function getDataChanged()
290 {
291 return $this->dataChanged;
292 }
293
294 /**
295 * Toggles device data-changed flag.
296 *
297 * @param bool $changed Whether data on device has changed.
298 *
299 * @return self
300 *
301 * @since 1.18.7 (J) - 1.8.7 (WP)
302 */
303 public function setDataChanged(bool $changed)
304 {
305 $this->dataChanged = $changed;
306
307 return $this;
308 }
309
310 /**
311 * Counts all connected listings by taking care of subunits.
312 *
313 * @return int
314 *
315 * @since 1.18.7 (J) - 1.8.7 (WP)
316 */
317 public function countConnectedListings()
318 {
319 $totalSubunitsCount = 0;
320 $excludeParents = 0;
321
322 foreach ($this->connectedSubunits as $listingId => $subunits) {
323 // count listing subunits
324 $totalSubunits = count($subunits);
325
326 // increase total subunits count
327 $totalSubunitsCount += $totalSubunits;
328
329 // increase parent listings to exclude
330 $excludeParents += $totalSubunits ? 1 : 0;
331 }
332
333 // return the accurate total connected listings count (inclusive of subunits)
334 return count($this->connectedListings) + $totalSubunitsCount - $excludeParents;
335 }
336
337 /**
338 * Counts the connected listing-subunits from the given list.
339 *
340 * @param array $listingSubunits Linear array of strings as "roomid-subunit".
341 *
342 * @return int Total number of device connected listing units.
343 *
344 * @since 1.18.7 (J) - 1.8.7 (WP)
345 */
346 public function countMatchingListingUnits(array $listingSubunits)
347 {
348 $totalCount = 0;
349
350 foreach ($listingSubunits as $listingSubunit) {
351 // extract listing ID and subunit number
352 $segments = explode('-', (string) $listingSubunit);
353 $listingId = (int) $segments[0];
354 $subunitId = (int) ($segments[1] ?? 0);
355
356 if (!in_array($listingId, $this->connectedListings)) {
357 // main listing ID not connected
358 continue;
359 }
360
361 if ($subunitId && ($this->connectedSubunits[$listingId] ?? [])) {
362 // make sure this exact subunit number is connected on the device
363 if (in_array($subunitId, $this->connectedSubunits[$listingId])) {
364 // match found, increase counter
365 $totalCount++;
366 }
367 } else {
368 // safely increase the counter for the matching listing ID
369 $totalCount++;
370 }
371 }
372
373 return $totalCount;
374 }
375
376 /**
377 * Given a list of listing-subunit pairs, intersects the available connections.
378 *
379 * @param array $listingSubunits Linear array of strings as "roomid-subunit".
380 * @param bool $unique True to return unique value-pairs.
381 *
382 * @return array List of intersecting listing-subunit pairs.
383 *
384 * @since 1.18.7 (J) - 1.8.7 (WP)
385 * @since 1.18.8 (J) - 1.8.8 (WP) added argument $unique.
386 */
387 public function intersectListingUnits(array $listingSubunits, bool $unique = true)
388 {
389 $intersections = [];
390
391 foreach ($listingSubunits as $listingSubunit) {
392 // extract listing ID and subunit number
393 $segments = explode('-', (string) $listingSubunit);
394 $listingId = (int) $segments[0];
395 $subunitId = (int) ($segments[1] ?? 0);
396
397 if (!in_array($listingId, $this->connectedListings)) {
398 // main listing ID not connected
399 continue;
400 }
401
402 if ($subunitId && ($this->connectedSubunits[$listingId] ?? [])) {
403 // make sure this exact subunit number is connected on the device
404 if (in_array($subunitId, $this->connectedSubunits[$listingId])) {
405 // intersection found for listing and sub-unit
406 $intersections[] = [$listingId, $subunitId];
407 }
408 } else {
409 // intersection found for listing
410 $intersections[] = [$listingId, 0];
411 }
412 }
413
414 if ($unique && count($intersections) > 1) {
415 /**
416 * Filter out duplicate value-pairs, useful in a multi-room booking context
417 * where a device is assigned to one room-type (no sub-unit) and it's booked
418 * multiple times. This helps reduce the number of passcodes generated.
419 */
420 $intersectStrings = array_map(function($intersect) {
421 // convert the pair into a comparable string for uniquely
422 return implode('-', $intersect);
423 }, $intersections);
424
425 // make the list of strings unique
426 $intersectStrings = array_values(array_unique($intersectStrings));
427
428 // re-convert the list into sub-arrays
429 $intersections = array_map(function($str) {
430 // get the pair of integers back
431 return array_map('intval', explode('-', $str));
432 }, $intersectStrings);
433 }
434
435 return $intersections;
436 }
437
438 /**
439 * Gets the listing IDs connected to the device.
440 *
441 * @return array Linear array of VikBooking listing IDs.
442 */
443 public function getConnectedListings()
444 {
445 return $this->connectedListings;
446 }
447
448 /**
449 * Sets the listing IDs connected to the device.
450 *
451 * @param array $listings List of VikBooking listing IDs.
452 *
453 * @return self
454 */
455 public function setConnectedListings(array $listings)
456 {
457 $this->connectedListings = array_values(
458 array_unique(
459 array_filter(
460 array_map('intval', $listings)
461 )
462 )
463 );
464
465 return $this;
466 }
467
468 /**
469 * Gets the listing subunit IDs connected to the device.
470 *
471 * @return array Associative list of listing sub-unit IDs.
472 *
473 * @since 1.18.7 (J) - 1.8.7 (WP)
474 */
475 public function getConnectedSubunits()
476 {
477 return $this->connectedSubunits;
478 }
479
480 /**
481 * Sets the subunit IDs connected to the device.
482 *
483 * @param array $subunits Associative list of listing sub-unit IDs.
484 *
485 * @return self
486 *
487 * @since 1.18.7 (J) - 1.8.7 (WP)
488 */
489 public function setConnectedSubunits(array $subunits)
490 {
491 $this->connectedSubunits = array_filter(array_map(function($list) {
492 return array_map('intval', (array) $list);
493 }, $subunits));
494
495 return $this;
496 }
497
498 /**
499 * Gets the subunit IDs of the given listing ID connected to the device.
500 *
501 * @param int $listingId The VikBooking listing ID.
502 *
503 * @return array Linear array of listing sub-unit IDs.
504 *
505 * @since 1.18.7 (J) - 1.8.7 (WP)
506 */
507 public function getConnectedListingSubunits(int $listingId)
508 {
509 return $this->connectedSubunits[$listingId] ?? [];
510 }
511
512 /**
513 * Adds an entry to the list of device connected listing IDs.
514 *
515 * @param int $listingId The listing ID to add as connected.
516 * @param ?int $subunitId Optional listing sub-unit ID (1-based).
517 *
518 * @return self
519 *
520 * @since 1.18.7 (J) - 1.8.7 (WP) added argument $subunitId.
521 */
522 public function addConnectedListing(int $listingId, ?int $subunitId = null)
523 {
524 if (!in_array($listingId, $this->connectedListings)) {
525 // push listing ID
526 $this->connectedListings[] = $listingId;
527 }
528
529 if ($subunitId) {
530 // push listing sub-unit ID relation
531 $this->connectedSubunits[$listingId] = $this->connectedSubunits[$listingId] ?? [];
532 if (!in_array($subunitId, $this->connectedSubunits[$listingId])) {
533 $this->connectedSubunits[$listingId][] = $subunitId;
534 }
535 }
536
537 return $this;
538 }
539
540 /**
541 * Removes an entry from the list of device connected listing IDs.
542 *
543 * @param int $listingId The listing ID to remove and disconnect.
544 *
545 * @return self
546 *
547 * @since 1.18.7 (J) - 1.8.7 (WP) added argument $subunitId.
548 */
549 public function removeConnectedListing(int $listingId, ?int $subunitId = null)
550 {
551 // process subunit-level connection first
552 if ($subunitId && ($this->connectedSubunits[$listingId] ?? [])) {
553 $this->connectedSubunits[$listingId] = array_values(array_filter($this->connectedSubunits[$listingId], function($currentSubunitId) use ($subunitId) {
554 return $currentSubunitId != $subunitId;
555 }));
556
557 if ($this->connectedSubunits[$listingId]) {
558 // the main listing still has some sub-units connected
559 // do not proceed
560 return $this;
561 }
562
563 // no more sub-units under this listing
564 // proceed with the listil-level connection removal for the whole listing
565 unset($this->connectedSubunits[$listingId]);
566 }
567
568 // process listing-level connection
569 $this->connectedListings = array_values(array_filter($this->connectedListings, function($currentId) use ($listingId) {
570 return $currentId != $listingId;
571 }));
572
573 return $this;
574 }
575
576 /**
577 * Gets the device capabilities.
578 *
579 * @return VBODooraccessDeviceCapability[]
580 */
581 public function getCapabilities()
582 {
583 return $this->capabilities;
584 }
585
586 /**
587 * Returns a specific capability from the current device.
588 *
589 * @param string $capabilityId The device capability identifier.
590 *
591 * @return VBODooraccessDeviceCapability
592 *
593 * @throws Exception
594 */
595 public function getCapabilityById(string $capabilityId)
596 {
597 foreach ($this->getCapabilities() as $cap) {
598 if ($cap->getID() == $capabilityId) {
599 return $cap;
600 }
601 }
602
603 throw new Exception(sprintf('Could not access the requested capability ID: %s.', $capabilityId), 404);
604 }
605
606 /**
607 * Tells if the device has got capabilities.
608 *
609 * @return bool
610 */
611 public function hasCapabilities()
612 {
613 return (bool) count($this->capabilities);
614 }
615
616 /**
617 * Sets a device capability.
618 *
619 * @param VBODooraccessDeviceCapability $capability
620 *
621 * @return self
622 *
623 * @throws Exception
624 */
625 public function setCapability(VBODooraccessDeviceCapability $capability)
626 {
627 if (!$capability->isValid()) {
628 throw new Exception('Capability is missing required information.', 500);
629 }
630
631 // push capability
632 $this->capabilities[] = $capability;
633
634 return $this;
635 }
636
637 /**
638 * Sets multiple device capability objects.
639 *
640 * @param VBODooraccessDeviceCapability[] $capabilities List of device capability objects.
641 *
642 * @return self
643 */
644 public function setCapabilities(array $capabilities)
645 {
646 foreach ($capabilities as $capability) {
647 $this->setCapability($capability);
648 }
649
650 return $this;
651 }
652
653 /**
654 * Resets the device capabilities.
655 *
656 * @return self
657 */
658 public function resetCapabilities()
659 {
660 $this->capabilities = [];
661
662 return $this;
663 }
664
665 /**
666 * Tells if the device has decorated the mandatory properties.
667 *
668 * @return bool
669 */
670 public function isComplete()
671 {
672 if (!$this->identifier || !$this->name) {
673 return false;
674 }
675
676 return true;
677 }
678 }
679