-
Notifications
You must be signed in to change notification settings - Fork 111
Add support for module PGM control #591
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a6b5f59
9c22cab
5fd3f0e
dd680c0
6741a2c
53d5eff
7326036
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,3 +47,23 @@ | |
| self.property = "on" | ||
|
|
||
| self.pai_entity_type = "pgm" | ||
|
|
||
|
|
||
| class ModulePGMSwitch(Switch): | ||
| def __init__(self, module_pgm, device, availability_topic: str): | ||
| super().__init__(device, availability_topic) | ||
| self.module_pgm = module_pgm | ||
|
|
||
| self.key = sanitize_key(module_pgm["key"]) | ||
| self.label = module_pgm["label"] | ||
| self.property = "on" | ||
|
|
||
| self.pai_entity_type = "pgm" | ||
|
|
||
| def serialize(self): | ||
| config = super().serialize() | ||
| config.update(dict( | ||
|
Check warning on line 65 in paradox/interfaces/mqtt/entities/switch.py
|
||
| payload_on="on_override", | ||
| payload_off="off_override", | ||
|
Comment on lines
+66
to
+67
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does not work with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This works for me switching "*_override" out for "off" and "on" - I get nothing. No relay click, no triggering of the attached device. This is for a relay on a PGM4. |
||
| )) | ||
| return config | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ | |
| async_loop_unhandled_exception_handler, | ||
| ) | ||
| from paradox.hardware import Panel, create_panel | ||
| from paradox.hardware.evo.parsers import MODULE_PGM_PACKET_SLOTS | ||
| from paradox.lib import ps | ||
| from paradox.lib.async_message_manager import ErrorMessageHandler, EventMessageHandler | ||
| from paradox.lib.handlers import PersistentHandler | ||
|
|
@@ -216,6 +217,7 @@ | |
|
|
||
| logger.info("Loading data from panel memory") | ||
| await self.panel.load_memory() | ||
| self._init_module_pgms() | ||
|
|
||
| logger.info("Running") | ||
| self.run_state = RunState.RUN | ||
|
|
@@ -489,33 +491,82 @@ | |
|
|
||
| return accepted | ||
|
|
||
| def _init_module_pgms(self): | ||
| for addr, pgm_count in cfg.MODULE_PGM_ADDRESSES.items(): | ||
| if not isinstance(addr, int) or not (1 <= addr <= 254): | ||
| logger.warning( | ||
| "MODULE_PGM_ADDRESSES: invalid module address %r (expected int 1-254), skipping", | ||
| addr, | ||
| ) | ||
| continue | ||
| if not isinstance(pgm_count, int) or not ( | ||
| 1 <= pgm_count <= MODULE_PGM_PACKET_SLOTS | ||
| ): | ||
| logger.warning( | ||
| "MODULE_PGM_ADDRESSES: invalid pgm_count %r for address %d (expected int 1-%d), skipping", | ||
| pgm_count, | ||
| addr, | ||
| MODULE_PGM_PACKET_SLOTS, | ||
| ) | ||
| continue | ||
| for pgm_index in range(1, pgm_count + 1): | ||
| key = f"module{addr}_pgm{pgm_index}" | ||
| self.storage.get_container("module_pgm")[key] = { | ||
| "id": pgm_index, | ||
| "key": key, | ||
| "label": f"Module {addr} PGM {pgm_index}", | ||
| "module_address": addr, | ||
| "pgm_index": pgm_index, | ||
| } | ||
| self.storage.update_container_object("module_pgm", key, {"on": False}) | ||
|
|
||
| async def control_output(self, output, command) -> bool: | ||
|
Check failure on line 523 in paradox/paradox.py
|
||
| command = command.lower() | ||
| logger.debug(f"Control Output: {output} - {command}") | ||
|
|
||
| outputs_selected = self.storage.get_container("pgm").select(output) | ||
| if outputs_selected: | ||
| accepted = False | ||
| try: | ||
| accepted = await self.panel.control_outputs(outputs_selected, command) | ||
| except NotImplementedError: | ||
| logger.error("control_output is not implemented for this alarm type") | ||
| except asyncio.CancelledError: | ||
| logger.error("control_output canceled") | ||
| raise | ||
| except asyncio.TimeoutError: | ||
| logger.error("control_output timeout") | ||
| self.request_status_refresh() | ||
| return accepted | ||
|
|
||
| module_pgm_selected = self.storage.get_container("module_pgm").select(output) | ||
| if module_pgm_selected: | ||
| accepted = False | ||
| module_pgm_container = self.storage.get_container("module_pgm") | ||
| for key in module_pgm_selected: | ||
| out = module_pgm_container[key] | ||
| try: | ||
| accepted = await self.panel.control_module_pgm_outputs( | ||
| out["module_address"], out["pgm_index"], command | ||
| ) | ||
| except NotImplementedError: | ||
| logger.error( | ||
| "control_module_pgm_outputs is not implemented for this alarm type" | ||
| ) | ||
| except asyncio.CancelledError: | ||
| logger.error("control_module_pgm_output canceled") | ||
| raise | ||
| except asyncio.TimeoutError: | ||
| logger.error("control_output timeout") | ||
| if accepted: | ||
| is_on = command in ("on", "on_override") | ||
| self.storage.update_container_object( | ||
| "module_pgm", out["key"], {"on": is_on} | ||
| ) | ||
| return accepted | ||
|
|
||
| # Not Found | ||
| if len(outputs_selected) == 0: | ||
| logger.error("No outputs selected") | ||
| return False | ||
|
|
||
| # Apply state changes | ||
| accepted = False | ||
| try: | ||
| accepted = await self.panel.control_outputs(outputs_selected, command) | ||
| except NotImplementedError: | ||
| logger.error("control_output is not implemented for this alarm type") | ||
| except asyncio.CancelledError: | ||
| logger.error("control_output canceled") | ||
| except asyncio.TimeoutError: | ||
| logger.error("control_output timeout") | ||
| # Apply state changes | ||
|
|
||
| # Refresh status | ||
| self.request_status_refresh() # Trigger status update | ||
|
|
||
| return accepted | ||
| logger.error("No outputs selected") | ||
| return False | ||
|
Comment on lines
523
to
+569
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this function requires a unit test. It is quite complicated. Especially how will it distinguish if it needs to trigger PGM on the main module or external module.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added some tests for this. |
||
|
|
||
| async def send_panic(self, partition_id, panic_type, user_id) -> bool: | ||
| logger.debug( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll include this change but I'm also renaming MODULE_PGM_OUTPUTS_PER_MODULE for clarity so I won't accept this suggestion exactly.