|
22 | 22 | async_loop_unhandled_exception_handler, |
23 | 23 | ) |
24 | 24 | from paradox.hardware import Panel, create_panel |
| 25 | +from paradox.hardware.evo.parsers import MODULE_PGM_PACKET_SLOTS |
25 | 26 | from paradox.lib import ps |
26 | 27 | from paradox.lib.async_message_manager import ErrorMessageHandler, EventMessageHandler |
27 | 28 | from paradox.lib.handlers import PersistentHandler |
@@ -216,6 +217,7 @@ async def full_connect(self) -> bool: |
216 | 217 |
|
217 | 218 | logger.info("Loading data from panel memory") |
218 | 219 | await self.panel.load_memory() |
| 220 | + self._init_module_pgms() |
219 | 221 |
|
220 | 222 | logger.info("Running") |
221 | 223 | self.run_state = RunState.RUN |
@@ -489,33 +491,82 @@ async def control_partition(self, partition: str, command: str) -> bool: |
489 | 491 |
|
490 | 492 | return accepted |
491 | 493 |
|
| 494 | + def _init_module_pgms(self): |
| 495 | + for addr, pgm_count in cfg.MODULE_PGM_ADDRESSES.items(): |
| 496 | + if not isinstance(addr, int) or not (1 <= addr <= 254): |
| 497 | + logger.warning( |
| 498 | + "MODULE_PGM_ADDRESSES: invalid module address %r (expected int 1-254), skipping", |
| 499 | + addr, |
| 500 | + ) |
| 501 | + continue |
| 502 | + if not isinstance(pgm_count, int) or not ( |
| 503 | + 1 <= pgm_count <= MODULE_PGM_PACKET_SLOTS |
| 504 | + ): |
| 505 | + logger.warning( |
| 506 | + "MODULE_PGM_ADDRESSES: invalid pgm_count %r for address %d (expected int 1-%d), skipping", |
| 507 | + pgm_count, |
| 508 | + addr, |
| 509 | + MODULE_PGM_PACKET_SLOTS, |
| 510 | + ) |
| 511 | + continue |
| 512 | + for pgm_index in range(1, pgm_count + 1): |
| 513 | + key = f"module{addr}_pgm{pgm_index}" |
| 514 | + self.storage.get_container("module_pgm")[key] = { |
| 515 | + "id": pgm_index, |
| 516 | + "key": key, |
| 517 | + "label": f"Module {addr} PGM {pgm_index}", |
| 518 | + "module_address": addr, |
| 519 | + "pgm_index": pgm_index, |
| 520 | + } |
| 521 | + self.storage.update_container_object("module_pgm", key, {"on": False}) |
| 522 | + |
492 | 523 | async def control_output(self, output, command) -> bool: |
493 | 524 | command = command.lower() |
494 | 525 | logger.debug(f"Control Output: {output} - {command}") |
495 | 526 |
|
496 | 527 | outputs_selected = self.storage.get_container("pgm").select(output) |
| 528 | + if outputs_selected: |
| 529 | + accepted = False |
| 530 | + try: |
| 531 | + accepted = await self.panel.control_outputs(outputs_selected, command) |
| 532 | + except NotImplementedError: |
| 533 | + logger.error("control_output is not implemented for this alarm type") |
| 534 | + except asyncio.CancelledError: |
| 535 | + logger.error("control_output canceled") |
| 536 | + raise |
| 537 | + except asyncio.TimeoutError: |
| 538 | + logger.error("control_output timeout") |
| 539 | + self.request_status_refresh() |
| 540 | + return accepted |
| 541 | + |
| 542 | + module_pgm_selected = self.storage.get_container("module_pgm").select(output) |
| 543 | + if module_pgm_selected: |
| 544 | + accepted = False |
| 545 | + module_pgm_container = self.storage.get_container("module_pgm") |
| 546 | + for key in module_pgm_selected: |
| 547 | + out = module_pgm_container[key] |
| 548 | + try: |
| 549 | + accepted = await self.panel.control_module_pgm_outputs( |
| 550 | + out["module_address"], out["pgm_index"], command |
| 551 | + ) |
| 552 | + except NotImplementedError: |
| 553 | + logger.error( |
| 554 | + "control_module_pgm_outputs is not implemented for this alarm type" |
| 555 | + ) |
| 556 | + except asyncio.CancelledError: |
| 557 | + logger.error("control_module_pgm_output canceled") |
| 558 | + raise |
| 559 | + except asyncio.TimeoutError: |
| 560 | + logger.error("control_output timeout") |
| 561 | + if accepted: |
| 562 | + is_on = command in ("on", "on_override") |
| 563 | + self.storage.update_container_object( |
| 564 | + "module_pgm", out["key"], {"on": is_on} |
| 565 | + ) |
| 566 | + return accepted |
497 | 567 |
|
498 | | - # Not Found |
499 | | - if len(outputs_selected) == 0: |
500 | | - logger.error("No outputs selected") |
501 | | - return False |
502 | | - |
503 | | - # Apply state changes |
504 | | - accepted = False |
505 | | - try: |
506 | | - accepted = await self.panel.control_outputs(outputs_selected, command) |
507 | | - except NotImplementedError: |
508 | | - logger.error("control_output is not implemented for this alarm type") |
509 | | - except asyncio.CancelledError: |
510 | | - logger.error("control_output canceled") |
511 | | - except asyncio.TimeoutError: |
512 | | - logger.error("control_output timeout") |
513 | | - # Apply state changes |
514 | | - |
515 | | - # Refresh status |
516 | | - self.request_status_refresh() # Trigger status update |
517 | | - |
518 | | - return accepted |
| 568 | + logger.error("No outputs selected") |
| 569 | + return False |
519 | 570 |
|
520 | 571 | async def send_panic(self, partition_id, panic_type, user_id) -> bool: |
521 | 572 | logger.debug( |
|
0 commit comments