elke27 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- elke27-0.1.2/.copier-answers.yml +9 -0
- elke27-0.1.2/.github/workflows/ci.yml +66 -0
- elke27-0.1.2/.github/workflows/publish.yml +47 -0
- elke27-0.1.2/.gitignore +188 -0
- elke27-0.1.2/.lock +0 -0
- elke27-0.1.2/CACHEDIR.TAG +1 -0
- elke27-0.1.2/LICENSE +21 -0
- elke27-0.1.2/Makefile +32 -0
- elke27-0.1.2/PKG-INFO +285 -0
- elke27-0.1.2/README.md +262 -0
- elke27-0.1.2/devtools/lint.py +53 -0
- elke27-0.1.2/docs/CHANGES.md +65 -0
- elke27-0.1.2/docs/CLIENT_CONTRACT.md +114 -0
- elke27-0.1.2/docs/Data-Update-Coordinator-Refactor.md +109 -0
- elke27-0.1.2/docs/Entity-Mapping-Checklist +61 -0
- elke27-0.1.2/docs/HA_LIBRARY_CONTRACT.md +247 -0
- elke27-0.1.2/docs/PERMISSIONS.md +136 -0
- elke27-0.1.2/docs/PUBLIC_API.md +84 -0
- elke27-0.1.2/docs/adr/ADR-0001.md +49 -0
- elke27-0.1.2/docs/adr/ADR-0002.md +47 -0
- elke27-0.1.2/docs/adr/ADR-0003.md +44 -0
- elke27-0.1.2/docs/adr/ADR-0004.md +35 -0
- elke27-0.1.2/docs/adr/ADR-0005.md +37 -0
- elke27-0.1.2/docs/adr/ADR-0006.md +39 -0
- elke27-0.1.2/docs/adr/ADR-0007.md +45 -0
- elke27-0.1.2/docs/adr/ADR-0008.md +36 -0
- elke27-0.1.2/docs/adr/ADR-0009.md +38 -0
- elke27-0.1.2/docs/adr/ADR-0010.md +41 -0
- elke27-0.1.2/docs/adr/ADR-0011.md +66 -0
- elke27-0.1.2/docs/adr/ADR-0012.md +53 -0
- elke27-0.1.2/docs/adr/ADR-0013.md +227 -0
- elke27-0.1.2/docs/adr/ADR-0013.md.old +80 -0
- elke27-0.1.2/docs/adr/ADR-0013.md.old2 +180 -0
- elke27-0.1.2/docs/adr/ADR-0100.md +59 -0
- elke27-0.1.2/docs/adr/ADR-0101.md +57 -0
- elke27-0.1.2/docs/adr/ADR-0102.md +64 -0
- elke27-0.1.2/docs/adr/ADR-0103.md +75 -0
- elke27-0.1.2/docs/adr/ADR-0104.md +82 -0
- elke27-0.1.2/docs/adr/ADR-0105.md +114 -0
- elke27-0.1.2/docs/adr/ADR-0106.md +81 -0
- elke27-0.1.2/docs/adr/ADR-0107.md +376 -0
- elke27-0.1.2/docs/adr/ADR-0108.md +72 -0
- elke27-0.1.2/docs/adr/ADR-0109.md +75 -0
- elke27-0.1.2/docs/adr/ADR-0110.md +96 -0
- elke27-0.1.2/docs/adr/ADR-0111.md +147 -0
- elke27-0.1.2/docs/adr/ADR-0112.md +125 -0
- elke27-0.1.2/docs/adr/ADR-0113.md +111 -0
- elke27-0.1.2/docs/adr/ADR-0114.md +133 -0
- elke27-0.1.2/docs/adr/ADR-0115.md +76 -0
- elke27-0.1.2/docs/adr/ADR-0116.md +70 -0
- elke27-0.1.2/docs/adr/ADR-0117.md +65 -0
- elke27-0.1.2/docs/adr/ADR-0118.md +75 -0
- elke27-0.1.2/docs/adr/ADR-0119.md +74 -0
- elke27-0.1.2/docs/adr/ADR-0120.md +83 -0
- elke27-0.1.2/docs/adr/ADR-0121.md +90 -0
- elke27-0.1.2/docs/adr/ADR-0122.md +122 -0
- elke27-0.1.2/docs/adr/ADR-0123.md +86 -0
- elke27-0.1.2/docs/adr/README.md +167 -0
- elke27-0.1.2/docs/client_contract_summary.md +63 -0
- elke27-0.1.2/docs/ddr/DDR-0001.md +42 -0
- elke27-0.1.2/docs/ddr/DDR-0002.md +35 -0
- elke27-0.1.2/docs/ddr/DDR-0003.md +46 -0
- elke27-0.1.2/docs/ddr/DDR-0004.md +37 -0
- elke27-0.1.2/docs/ddr/DDR-0005.md +32 -0
- elke27-0.1.2/docs/ddr/DDR-0006.md +33 -0
- elke27-0.1.2/docs/ddr/DDR-0007.md +37 -0
- elke27-0.1.2/docs/ddr/DDR-0008.md +37 -0
- elke27-0.1.2/docs/ddr/DDR-0009.md +43 -0
- elke27-0.1.2/docs/ddr/DDR-0010.md +44 -0
- elke27-0.1.2/docs/ddr/DDR-0011.md +44 -0
- elke27-0.1.2/docs/ddr/DDR-0012.md +46 -0
- elke27-0.1.2/docs/ddr/DDR-0013.md +38 -0
- elke27-0.1.2/docs/ddr/DDR-0014.md +48 -0
- elke27-0.1.2/docs/ddr/DDR-0015.md +53 -0
- elke27-0.1.2/docs/ddr/DDR-0016.md +52 -0
- elke27-0.1.2/docs/ddr/DDR-0017.md +51 -0
- elke27-0.1.2/docs/ddr/DDR-0018.md +44 -0
- elke27-0.1.2/docs/ddr/DDR-0019.md +72 -0
- elke27-0.1.2/docs/ddr/DDR-0020.md +53 -0
- elke27-0.1.2/docs/ddr/DDR-0021.md +63 -0
- elke27-0.1.2/docs/ddr/DDR-0022.md +141 -0
- elke27-0.1.2/docs/ddr/DDR-0023.md +84 -0
- elke27-0.1.2/docs/ddr/DDR-0024.md +87 -0
- elke27-0.1.2/docs/ddr/DDR-0025.md +129 -0
- elke27-0.1.2/docs/ddr/DDR-0026.md +27 -0
- elke27-0.1.2/docs/ddr/DDR-0027.md +145 -0
- elke27-0.1.2/docs/ddr/DDR-0028.md +178 -0
- elke27-0.1.2/docs/ddr/DDR-0029.md +45 -0
- elke27-0.1.2/docs/ddr/DDR-0030.md +48 -0
- elke27-0.1.2/docs/ddr/DDR-0031.md +43 -0
- elke27-0.1.2/docs/ddr/DDR-0032.md +55 -0
- elke27-0.1.2/docs/ddr/DDR-0033.md +74 -0
- elke27-0.1.2/docs/ddr/DDR-0034.md +113 -0
- elke27-0.1.2/docs/ddr/DDR-0035.md +77 -0
- elke27-0.1.2/docs/ddr/DDR-0036.md +166 -0
- elke27-0.1.2/docs/ddr/DDR-0037.md +223 -0
- elke27-0.1.2/docs/ddr/DDR-0038.md +55 -0
- elke27-0.1.2/docs/ddr/DDR-0039.md +56 -0
- elke27-0.1.2/docs/ddr/DDR-0040.md +51 -0
- elke27-0.1.2/docs/ddr/DDR-0041.md +69 -0
- elke27-0.1.2/docs/ddr/README.md +49 -0
- elke27-0.1.2/docs/development.md +94 -0
- elke27-0.1.2/docs/installation.md +27 -0
- elke27-0.1.2/docs/publishing.md +205 -0
- elke27-0.1.2/docs/tdr/README.md +78 -0
- elke27-0.1.2/docs/tdr/TDR0001.md +76 -0
- elke27-0.1.2/docs/tdr/TDR0002.md +54 -0
- elke27-0.1.2/docs/tdr/TDR0003.md +54 -0
- elke27-0.1.2/docs/tdr/TDR0004.md +58 -0
- elke27-0.1.2/examples/e27_aioscanner.py +49 -0
- elke27-0.1.2/examples/e27_api_link.py +146 -0
- elke27-0.1.2/examples/e27_client_contract.py +150 -0
- elke27-0.1.2/examples/e27_client_contract_example.py +231 -0
- elke27-0.1.2/examples/e27_live_version_info.py +206 -0
- elke27-0.1.2/examples/e27_network_param_live.py +227 -0
- elke27-0.1.2/examples/e27_simple_program.py +138 -0
- elke27-0.1.2/examples/e27_unsolicited_monitor.py +133 -0
- elke27-0.1.2/examples/elk-e27-env-vars.sh +17 -0
- elke27-0.1.2/pyproject.toml +182 -0
- elke27-0.1.2/src/elke27_lib/__init__.py +76 -0
- elke27-0.1.2/src/elke27_lib/client.py +2179 -0
- elke27-0.1.2/src/elke27_lib/const.py +52 -0
- elke27-0.1.2/src/elke27_lib/discovery.py +205 -0
- elke27-0.1.2/src/elke27_lib/dispatcher.py +767 -0
- elke27-0.1.2/src/elke27_lib/encryption.py +280 -0
- elke27-0.1.2/src/elke27_lib/errors.py +651 -0
- elke27-0.1.2/src/elke27_lib/events.py +455 -0
- elke27-0.1.2/src/elke27_lib/features/area.py +165 -0
- elke27-0.1.2/src/elke27_lib/features/bus_ios.py +37 -0
- elke27-0.1.2/src/elke27_lib/features/control.py +82 -0
- elke27-0.1.2/src/elke27_lib/features/keypad.py +70 -0
- elke27-0.1.2/src/elke27_lib/features/log.py +131 -0
- elke27-0.1.2/src/elke27_lib/features/network_param.py +55 -0
- elke27-0.1.2/src/elke27_lib/features/output.py +124 -0
- elke27-0.1.2/src/elke27_lib/features/rule.py +35 -0
- elke27-0.1.2/src/elke27_lib/features/system.py +385 -0
- elke27-0.1.2/src/elke27_lib/features/tstat.py +57 -0
- elke27-0.1.2/src/elke27_lib/features/user.py +56 -0
- elke27-0.1.2/src/elke27_lib/features/zone.py +187 -0
- elke27-0.1.2/src/elke27_lib/framing.py +269 -0
- elke27-0.1.2/src/elke27_lib/generators/__init__.py +23 -0
- elke27-0.1.2/src/elke27_lib/generators/all_generators.py +73 -0
- elke27-0.1.2/src/elke27_lib/generators/area.py +52 -0
- elke27-0.1.2/src/elke27_lib/generators/bus_ios.py +9 -0
- elke27-0.1.2/src/elke27_lib/generators/control.py +23 -0
- elke27-0.1.2/src/elke27_lib/generators/keypad.py +21 -0
- elke27-0.1.2/src/elke27_lib/generators/log.py +50 -0
- elke27-0.1.2/src/elke27_lib/generators/network_param.py +13 -0
- elke27-0.1.2/src/elke27_lib/generators/output.py +45 -0
- elke27-0.1.2/src/elke27_lib/generators/registry.py +160 -0
- elke27-0.1.2/src/elke27_lib/generators/rule.py +11 -0
- elke27-0.1.2/src/elke27_lib/generators/system.py +158 -0
- elke27-0.1.2/src/elke27_lib/generators/tstat.py +15 -0
- elke27-0.1.2/src/elke27_lib/generators/user.py +17 -0
- elke27-0.1.2/src/elke27_lib/generators/zone.py +51 -0
- elke27-0.1.2/src/elke27_lib/handlers/all_handlers.py +73 -0
- elke27-0.1.2/src/elke27_lib/handlers/area.py +1176 -0
- elke27-0.1.2/src/elke27_lib/handlers/bus_ios.py +96 -0
- elke27-0.1.2/src/elke27_lib/handlers/control.py +484 -0
- elke27-0.1.2/src/elke27_lib/handlers/keypad.py +352 -0
- elke27-0.1.2/src/elke27_lib/handlers/log.py +346 -0
- elke27-0.1.2/src/elke27_lib/handlers/network_param.py +308 -0
- elke27-0.1.2/src/elke27_lib/handlers/output.py +556 -0
- elke27-0.1.2/src/elke27_lib/handlers/rule.py +102 -0
- elke27-0.1.2/src/elke27_lib/handlers/system.py +552 -0
- elke27-0.1.2/src/elke27_lib/handlers/tstat.py +299 -0
- elke27-0.1.2/src/elke27_lib/handlers/user.py +238 -0
- elke27-0.1.2/src/elke27_lib/handlers/zone.py +1291 -0
- elke27-0.1.2/src/elke27_lib/hello.py +244 -0
- elke27-0.1.2/src/elke27_lib/kernel.py +1602 -0
- elke27-0.1.2/src/elke27_lib/linking.py +497 -0
- elke27-0.1.2/src/elke27_lib/message.py +41 -0
- elke27-0.1.2/src/elke27_lib/outbound.py +208 -0
- elke27-0.1.2/src/elke27_lib/pending.py +100 -0
- elke27-0.1.2/src/elke27_lib/permissions.py +512 -0
- elke27-0.1.2/src/elke27_lib/presentation.py +369 -0
- elke27-0.1.2/src/elke27_lib/provisioning.py +51 -0
- elke27-0.1.2/src/elke27_lib/redact.py +105 -0
- elke27-0.1.2/src/elke27_lib/session.py +721 -0
- elke27-0.1.2/src/elke27_lib/states.py +369 -0
- elke27-0.1.2/src/elke27_lib/types.py +255 -0
- elke27-0.1.2/src/elke27_lib/util.py +111 -0
- elke27-0.1.2/test/__init__.py +0 -0
- elke27-0.1.2/test/conftest.py +309 -0
- elke27-0.1.2/test/helpers/__init__.py +0 -0
- elke27-0.1.2/test/helpers/dispatch.py +20 -0
- elke27-0.1.2/test/helpers/error_codes.py +36 -0
- elke27-0.1.2/test/helpers/internal.py +15 -0
- elke27-0.1.2/test/helpers/live.py +23 -0
- elke27-0.1.2/test/helpers/payload_validation.py +227 -0
- elke27-0.1.2/test/helpers/redaction.py +95 -0
- elke27-0.1.2/test/helpers/reporter.py +200 -0
- elke27-0.1.2/test/helpers/trace.py +114 -0
- elke27-0.1.2/test/test_area_attribs_names.py +33 -0
- elke27-0.1.2/test/test_area_get_configured_block_id.py +26 -0
- elke27-0.1.2/test/test_area_get_trouble_route.py +26 -0
- elke27-0.1.2/test/test_bootstrap_readiness.py +197 -0
- elke27-0.1.2/test/test_csm_snapshot.py +87 -0
- elke27-0.1.2/test/test_dispatcher_paged_missing_block_id.py +33 -0
- elke27-0.1.2/test/test_e27_area_get_status.py +55 -0
- elke27-0.1.2/test/test_e27_area_get_status_live.py +86 -0
- elke27-0.1.2/test/test_e27_area_info_live.py +112 -0
- elke27-0.1.2/test/test_e27_area_set_arm_state_async_execute.py +176 -0
- elke27-0.1.2/test/test_e27_area_set_status_async_execute.py +95 -0
- elke27-0.1.2/test/test_e27_async_execute_paged.py +128 -0
- elke27-0.1.2/test/test_e27_async_execute_permissions.py +92 -0
- elke27-0.1.2/test/test_e27_async_execute_seq.py +109 -0
- elke27-0.1.2/test/test_e27_configured_inventory.py +315 -0
- elke27-0.1.2/test/test_e27_configured_names_live.py +237 -0
- elke27-0.1.2/test/test_e27_conftest.save +15 -0
- elke27-0.1.2/test/test_e27_discovery_local_parse.py +13 -0
- elke27-0.1.2/test/test_e27_dispatcher.py +369 -0
- elke27-0.1.2/test/test_e27_framing.py +476 -0
- elke27-0.1.2/test/test_e27_framing_presentation_roundtrip.py +52 -0
- elke27-0.1.2/test/test_e27_hello_concat_json.py +78 -0
- elke27-0.1.2/test/test_e27_hello_roundtrip.py +79 -0
- elke27-0.1.2/test/test_e27_link_raw_buffers.py +115 -0
- elke27-0.1.2/test/test_e27_network_param_live.py +93 -0
- elke27-0.1.2/test/test_e27_output_tstat_live.py +110 -0
- elke27-0.1.2/test/test_e27_presentation.py +195 -0
- elke27-0.1.2/test/test_e27_retry_seq.py +49 -0
- elke27-0.1.2/test/test_e27_roundtrip.py +157 -0
- elke27-0.1.2/test/test_e27_seq.py +13 -0
- elke27-0.1.2/test/test_e27_session.py +642 -0
- elke27-0.1.2/test/test_e27_table_info_live.py +116 -0
- elke27-0.1.2/test/test_e27_util.py +117 -0
- elke27-0.1.2/test/test_e27_zone_defs_live.py +102 -0
- elke27-0.1.2/test/test_e27_zone_live.py +83 -0
- elke27-0.1.2/test/test_e27_zone_status_string.py +46 -0
- elke27-0.1.2/test/test_generators_registry.py +150 -0
- elke27-0.1.2/test/test_handler_naming.py +8 -0
- elke27-0.1.2/test/test_kernel_close_noise.py +40 -0
- elke27-0.1.2/test/test_kernel_request_state.py +209 -0
- elke27-0.1.2/test/test_keypad_enumeration.py +72 -0
- elke27-0.1.2/test/test_live_e27_area_names.py +59 -0
- elke27-0.1.2/test/test_live_e27_arm_stay_then_disarm.py +94 -0
- elke27-0.1.2/test/test_live_e27_authenticate.py +65 -0
- elke27-0.1.2/test/test_live_e27_bypass_events.py +60 -0
- elke27-0.1.2/test/test_live_e27_chime.py +121 -0
- elke27-0.1.2/test/test_live_e27_keepalive.py +39 -0
- elke27-0.1.2/test/test_live_e27_keypads_download.py +79 -0
- elke27-0.1.2/test/test_live_e27_link.py +47 -0
- elke27-0.1.2/test/test_live_e27_log.py +140 -0
- elke27-0.1.2/test/test_live_e27_output_names.py +62 -0
- elke27-0.1.2/test/test_live_e27_paged_readonly.py +41 -0
- elke27-0.1.2/test/test_live_e27_rules_download.py +33 -0
- elke27-0.1.2/test/test_live_e27_system_status.py +62 -0
- elke27-0.1.2/test/test_live_e27_users_download.py +134 -0
- elke27-0.1.2/test/test_live_e27_zone_bypass.py +80 -0
- elke27-0.1.2/test/test_live_e27_zone_names.py +63 -0
- elke27-0.1.2/test/test_live_e27_zone_status_flags.py +50 -0
- elke27-0.1.2/test/test_log_domain.py +184 -0
- elke27-0.1.2/test/test_outbound_queue.py +137 -0
- elke27-0.1.2/test/test_output_attribs_names.py +33 -0
- elke27-0.1.2/test/test_permissions.py +83 -0
- elke27-0.1.2/test/test_placeholder.py +3 -0
- elke27-0.1.2/test/test_public_api_imports.py +226 -0
- elke27-0.1.2/test/test_root_error_envelope.py +20 -0
- elke27-0.1.2/test/test_rule_get_rules_paging.py +149 -0
- elke27-0.1.2/test/test_system_status_snapshot.py +49 -0
- elke27-0.1.2/test/test_timeout_start_on_send.py +163 -0
- elke27-0.1.2/test/test_trouble_handlers_generators.py +77 -0
- elke27-0.1.2/test/test_user_enumeration.py +72 -0
- elke27-0.1.2/test/test_v2_commands.py +136 -0
- elke27-0.1.2/test/test_v2_events_snapshot.py +496 -0
- elke27-0.1.2/test/test_v2_wait_ready.py +86 -0
- elke27-0.1.2/test/test_zone_attribs_names.py +42 -0
- elke27-0.1.2/test/test_zone_bulk_status_configured.py +35 -0
- elke27-0.1.2/test/test_zone_get_configured_paging_merge.py +51 -0
- elke27-0.1.2/test/test_zone_status_flags.py +76 -0
- elke27-0.1.2/uv.lock +447 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changes here will be overwritten by Copier. Do not edit manually.
|
|
2
|
+
_commit: v0.2.21
|
|
3
|
+
_src_path: gh:jlevy/simple-modern-uv
|
|
4
|
+
package_author_email: mitchmitchell1616@msn.com
|
|
5
|
+
package_author_name: mitchmitchell
|
|
6
|
+
package_description: Library for interfacing with the Elk Products E27 Alarm Engine
|
|
7
|
+
package_github_org: mitchmitchell
|
|
8
|
+
package_module: elke27_lib
|
|
9
|
+
package_name: elke27
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
|
3
|
+
|
|
4
|
+
name: CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
# Use ["main", "master"] for CI only on the default branch.
|
|
9
|
+
# Use ["**"] for CI on all branches.
|
|
10
|
+
branches: ["main", "master"]
|
|
11
|
+
pull_request:
|
|
12
|
+
branches: ["main", "master"]
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
strategy:
|
|
20
|
+
matrix:
|
|
21
|
+
# Update this as needed:
|
|
22
|
+
# Common platforms: ["ubuntu-latest", "macos-latest", "windows-latest"]
|
|
23
|
+
os: ["ubuntu-latest"]
|
|
24
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
25
|
+
|
|
26
|
+
# Linux only by default. Use ${{ matrix.os }} for other OSes.
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
# Generally following uv docs:
|
|
31
|
+
# https://docs.astral.sh/uv/guides/integration/github/
|
|
32
|
+
|
|
33
|
+
- name: Checkout (official GitHub action)
|
|
34
|
+
uses: actions/checkout@v6
|
|
35
|
+
with:
|
|
36
|
+
# Important for versioning plugins:
|
|
37
|
+
fetch-depth: 0
|
|
38
|
+
|
|
39
|
+
- name: Remove accidental venv checked into repo
|
|
40
|
+
run: rm -rf bin include pyvenv.cfg lib lib64 .venv
|
|
41
|
+
|
|
42
|
+
- name: Install uv (official Astral action)
|
|
43
|
+
uses: astral-sh/setup-uv@v7
|
|
44
|
+
with:
|
|
45
|
+
# Update this as needed:
|
|
46
|
+
version: "0.9.25"
|
|
47
|
+
enable-cache: true
|
|
48
|
+
python-version: ${{ matrix.python-version }}
|
|
49
|
+
|
|
50
|
+
- name: Set up Python (using uv)
|
|
51
|
+
run: uv python install
|
|
52
|
+
|
|
53
|
+
# Alternately can use the official Python action:
|
|
54
|
+
# - name: Set up Python (using actions/setup-python)
|
|
55
|
+
# uses: actions/setup-python@v5
|
|
56
|
+
# with:
|
|
57
|
+
# python-version: ${{ matrix.python-version }}
|
|
58
|
+
|
|
59
|
+
- name: Install all dependencies
|
|
60
|
+
run: uv sync --all-extras
|
|
61
|
+
|
|
62
|
+
- name: Run linting
|
|
63
|
+
run: uv run python devtools/lint.py
|
|
64
|
+
|
|
65
|
+
- name: Run tests
|
|
66
|
+
run: uv run pytest
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch: # Enable manual trigger.
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # Mandatory for OIDC.
|
|
13
|
+
contents: read
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout (official GitHub action)
|
|
16
|
+
uses: actions/checkout@v6
|
|
17
|
+
with:
|
|
18
|
+
# Important for versioning plugins:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- name: Remove accidental venv checked into repo
|
|
22
|
+
run: rm -rf bin include pyvenv.cfg lib lib64 .venv
|
|
23
|
+
|
|
24
|
+
- name: Install uv (official Astral action)
|
|
25
|
+
uses: astral-sh/setup-uv@v7
|
|
26
|
+
with:
|
|
27
|
+
version: "0.9.25"
|
|
28
|
+
enable-cache: true
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
|
|
31
|
+
- name: Set up Python (using uv)
|
|
32
|
+
run: uv python install
|
|
33
|
+
|
|
34
|
+
- name: Install all dependencies
|
|
35
|
+
run: uv sync --all-extras
|
|
36
|
+
|
|
37
|
+
- name: Run tests
|
|
38
|
+
run: uv run pytest
|
|
39
|
+
|
|
40
|
+
- name: Build package
|
|
41
|
+
run: uv build
|
|
42
|
+
|
|
43
|
+
- name: Publish to PyPI
|
|
44
|
+
run: uv publish --trusted-publishing always
|
|
45
|
+
# Although uv is newer and faster, the "official" publishing option is the one from PyPA,
|
|
46
|
+
# which uses twine. If desired, replace `uv publish` with:
|
|
47
|
+
# uses: pypa/gh-action-pypi-publish@release/v1
|
elke27-0.1.2/.gitignore
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Additions to standard GitHub .gitignore:
|
|
2
|
+
*.bak
|
|
3
|
+
*.orig
|
|
4
|
+
tmp/
|
|
5
|
+
trash/
|
|
6
|
+
attic/
|
|
7
|
+
.kash/
|
|
8
|
+
|
|
9
|
+
# test run logs
|
|
10
|
+
artifacts/test_runs/*.jsonl
|
|
11
|
+
test/artifacts/test_runs/*.jsonl
|
|
12
|
+
|
|
13
|
+
# Byte-compiled / optimized / DLL files
|
|
14
|
+
__pycache__/
|
|
15
|
+
*.py[cod]
|
|
16
|
+
*$py.class
|
|
17
|
+
|
|
18
|
+
# C extensions
|
|
19
|
+
*.so
|
|
20
|
+
|
|
21
|
+
# Distribution / packaging
|
|
22
|
+
.Python
|
|
23
|
+
build/
|
|
24
|
+
develop-eggs/
|
|
25
|
+
dist/
|
|
26
|
+
downloads/
|
|
27
|
+
eggs/
|
|
28
|
+
.eggs/
|
|
29
|
+
lib/
|
|
30
|
+
lib64/
|
|
31
|
+
parts/
|
|
32
|
+
sdist/
|
|
33
|
+
var/
|
|
34
|
+
wheels/
|
|
35
|
+
share/python-wheels/
|
|
36
|
+
*.egg-info/
|
|
37
|
+
.installed.cfg
|
|
38
|
+
*.egg
|
|
39
|
+
MANIFEST
|
|
40
|
+
|
|
41
|
+
# PyInstaller
|
|
42
|
+
# Usually these files are written by a python script from a template
|
|
43
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
44
|
+
*.manifest
|
|
45
|
+
*.spec
|
|
46
|
+
|
|
47
|
+
# Installer logs
|
|
48
|
+
pip-log.txt
|
|
49
|
+
pip-delete-this-directory.txt
|
|
50
|
+
|
|
51
|
+
# Unit test / coverage reports
|
|
52
|
+
htmlcov/
|
|
53
|
+
.tox/
|
|
54
|
+
.nox/
|
|
55
|
+
.coverage
|
|
56
|
+
.coverage.*
|
|
57
|
+
.cache
|
|
58
|
+
nosetests.xml
|
|
59
|
+
coverage.xml
|
|
60
|
+
*.cover
|
|
61
|
+
*.py,cover
|
|
62
|
+
.hypothesis/
|
|
63
|
+
.pytest_cache/
|
|
64
|
+
cover/
|
|
65
|
+
|
|
66
|
+
# Translations
|
|
67
|
+
*.mo
|
|
68
|
+
*.pot
|
|
69
|
+
|
|
70
|
+
# Django stuff:
|
|
71
|
+
*.log
|
|
72
|
+
local_settings.py
|
|
73
|
+
db.sqlite3
|
|
74
|
+
db.sqlite3-journal
|
|
75
|
+
|
|
76
|
+
# Flask stuff:
|
|
77
|
+
instance/
|
|
78
|
+
.webassets-cache
|
|
79
|
+
|
|
80
|
+
# Scrapy stuff:
|
|
81
|
+
.scrapy
|
|
82
|
+
|
|
83
|
+
# Sphinx documentation
|
|
84
|
+
docs/_build/
|
|
85
|
+
|
|
86
|
+
# PyBuilder
|
|
87
|
+
.pybuilder/
|
|
88
|
+
target/
|
|
89
|
+
|
|
90
|
+
# Jupyter Notebook
|
|
91
|
+
.ipynb_checkpoints
|
|
92
|
+
|
|
93
|
+
# IPython
|
|
94
|
+
profile_default/
|
|
95
|
+
ipython_config.py
|
|
96
|
+
|
|
97
|
+
# pyenv
|
|
98
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
99
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
100
|
+
# .python-version
|
|
101
|
+
|
|
102
|
+
# pipenv
|
|
103
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
104
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
105
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
106
|
+
# install all needed dependencies.
|
|
107
|
+
#Pipfile.lock
|
|
108
|
+
|
|
109
|
+
# UV
|
|
110
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
111
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
112
|
+
# commonly ignored for libraries.
|
|
113
|
+
#uv.lock
|
|
114
|
+
|
|
115
|
+
# poetry
|
|
116
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
117
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
118
|
+
# commonly ignored for libraries.
|
|
119
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
120
|
+
#poetry.lock
|
|
121
|
+
|
|
122
|
+
# pdm
|
|
123
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
124
|
+
#pdm.lock
|
|
125
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
126
|
+
# in version control.
|
|
127
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
128
|
+
.pdm.toml
|
|
129
|
+
.pdm-python
|
|
130
|
+
.pdm-build/
|
|
131
|
+
|
|
132
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
133
|
+
__pypackages__/
|
|
134
|
+
|
|
135
|
+
# Celery stuff
|
|
136
|
+
celerybeat-schedule
|
|
137
|
+
celerybeat.pid
|
|
138
|
+
|
|
139
|
+
# SageMath parsed files
|
|
140
|
+
*.sage.py
|
|
141
|
+
|
|
142
|
+
# Environments
|
|
143
|
+
.env
|
|
144
|
+
.venv
|
|
145
|
+
env/
|
|
146
|
+
venv/
|
|
147
|
+
ENV/
|
|
148
|
+
env.bak/
|
|
149
|
+
venv.bak/
|
|
150
|
+
|
|
151
|
+
# Virtualenvs created directly in the repo root (critical for uv / CI)
|
|
152
|
+
bin/
|
|
153
|
+
include/
|
|
154
|
+
pyvenv.cfg
|
|
155
|
+
|
|
156
|
+
# Spyder project settings
|
|
157
|
+
.spyderproject
|
|
158
|
+
.spyproject
|
|
159
|
+
|
|
160
|
+
# Rope project settings
|
|
161
|
+
.ropeproject
|
|
162
|
+
|
|
163
|
+
# mkdocs documentation
|
|
164
|
+
/site
|
|
165
|
+
|
|
166
|
+
# mypy
|
|
167
|
+
.mypy_cache/
|
|
168
|
+
.dmypy.json
|
|
169
|
+
dmypy.json
|
|
170
|
+
|
|
171
|
+
# Pyre type checker
|
|
172
|
+
.pyre/
|
|
173
|
+
|
|
174
|
+
# pytype static type analyzer
|
|
175
|
+
.pytype/
|
|
176
|
+
|
|
177
|
+
# Cython debug symbols
|
|
178
|
+
cython_debug/
|
|
179
|
+
|
|
180
|
+
# PyCharm
|
|
181
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
182
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
183
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
184
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
185
|
+
#.idea/
|
|
186
|
+
|
|
187
|
+
# PyPI configuration file
|
|
188
|
+
.pypirc
|
elke27-0.1.2/.lock
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Signature: 8a477f597d28d172789f06886806bc55
|
elke27-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 mitchmitchell
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
elke27-0.1.2/Makefile
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Makefile for easy development workflows.
|
|
2
|
+
# See docs/development.md for docs.
|
|
3
|
+
# Note GitHub Actions call uv directly, not this Makefile.
|
|
4
|
+
|
|
5
|
+
.DEFAULT_GOAL := default
|
|
6
|
+
|
|
7
|
+
.PHONY: default install lint test upgrade build clean
|
|
8
|
+
|
|
9
|
+
default: install lint test
|
|
10
|
+
|
|
11
|
+
install:
|
|
12
|
+
uv sync --all-extras
|
|
13
|
+
|
|
14
|
+
lint:
|
|
15
|
+
uv run python devtools/lint.py
|
|
16
|
+
|
|
17
|
+
test:
|
|
18
|
+
uv run pytest
|
|
19
|
+
|
|
20
|
+
upgrade:
|
|
21
|
+
uv sync --upgrade --all-extras --dev
|
|
22
|
+
|
|
23
|
+
build:
|
|
24
|
+
uv build
|
|
25
|
+
|
|
26
|
+
clean:
|
|
27
|
+
-rm -rf dist/
|
|
28
|
+
-rm -rf *.egg-info/
|
|
29
|
+
-rm -rf .pytest_cache/
|
|
30
|
+
-rm -rf .mypy_cache/
|
|
31
|
+
-rm -rf .venv/
|
|
32
|
+
-find . -type d -name "__pycache__" -exec rm -rf {} +
|
elke27-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: elke27
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Library for interfacing with the Elk Products E27 Alarm Engine
|
|
5
|
+
Project-URL: Repository, https://github.com/mitchmitchell/elke27
|
|
6
|
+
Author-email: mitchmitchell <mitchmitchell1616@msn.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: <4.0,>=3.11
|
|
20
|
+
Requires-Dist: cryptography>=42.0.0
|
|
21
|
+
Requires-Dist: typing-extensions>=4.9.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# elke27
|
|
25
|
+
|
|
26
|
+
# elke27_lib
|
|
27
|
+
|
|
28
|
+
`elke27_lib` is a Python 3.11+ library for interfacing with the **Elk Products E27 Alarm Engine** over the E27 IP protocol. It is designed to be consumed by **Home Assistant** (and standalone tools) while keeping the protocol implementation encapsulated behind a stable client API.
|
|
29
|
+
|
|
30
|
+
This repository also includes:
|
|
31
|
+
- a comprehensive pytest suite (unit + live-on-panel tests)
|
|
32
|
+
- example programs for discovery, linking, connecting, event monitoring, and basic API usage
|
|
33
|
+
- ADRs and protocol notes used to guide implementation
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## What this library does
|
|
38
|
+
|
|
39
|
+
### Core capabilities
|
|
40
|
+
- **Discovery**: Find E27 panels on the LAN and return identity details (panel name, MAC, serial, host/port, etc.).
|
|
41
|
+
- **Linking (provisioning)**: Perform the E27 *API_LINK* flow to obtain long-lived link keys (encryption + HMAC keys) used to establish trusted sessions.
|
|
42
|
+
- **Session establishment**: Connect to a panel, perform HELLO + session bootstrap, and transition to a ready state.
|
|
43
|
+
- **Event-driven updates**: Receive unsolicited panel messages and dispatch them as structured events.
|
|
44
|
+
- **Command execution**: Send API requests and receive responses using a strict one-in-flight model (request/response sequencing).
|
|
45
|
+
- **Snapshots**: Provide read-only state “snapshots” for Home Assistant (or other clients) to read stable views of current panel state.
|
|
46
|
+
|
|
47
|
+
### Stable public surface for Home Assistant
|
|
48
|
+
Home Assistant is intended to import and use **only** the public client API defined in:
|
|
49
|
+
|
|
50
|
+
- `docs/CLIENT_CONTRACT.md`
|
|
51
|
+
|
|
52
|
+
As described there:
|
|
53
|
+
- Home Assistant imports only:
|
|
54
|
+
- `from elke27_lib.client import Elke27Client, Result`
|
|
55
|
+
- Home Assistant must **not** import internal modules (session, dispatcher, features, handlers, etc.).
|
|
56
|
+
- The contract is treated as stable during refactors.
|
|
57
|
+
|
|
58
|
+
If you are building a Home Assistant integration, start by reading:
|
|
59
|
+
- `docs/CLIENT_CONTRACT.md`
|
|
60
|
+
- `docs/PERMISSIONS.md`
|
|
61
|
+
- `docs/adr/*`
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## High-level architecture (mental model)
|
|
66
|
+
|
|
67
|
+
### 1) Discovery
|
|
68
|
+
Discovery is used to locate panels and present them to the user by **panel name**, while binding identity definitively by **MAC address**.
|
|
69
|
+
|
|
70
|
+
Implementation entry points:
|
|
71
|
+
- `elke27_lib.discovery`
|
|
72
|
+
- `Elk.discover()` (facade used in examples)
|
|
73
|
+
|
|
74
|
+
### 2) Linking (API_LINK provisioning)
|
|
75
|
+
Linking is a provisioning-time operation that produces long-lived keys. These keys are persisted externally (for example, by Home Assistant) and reused on subsequent connections.
|
|
76
|
+
|
|
77
|
+
Implementation entry points:
|
|
78
|
+
- `Elke27Client.async_link(...)`
|
|
79
|
+
- `Elk.link(...)` (facade used in examples)
|
|
80
|
+
|
|
81
|
+
### 3) Session connect + readiness
|
|
82
|
+
Connecting establishes a framed/encrypted session, performs session bootstrap, and transitions the client to “ready”.
|
|
83
|
+
|
|
84
|
+
Per the client contract, readiness is defined by:
|
|
85
|
+
- Session ACTIVE, AND
|
|
86
|
+
- `panel_info.session_id` present, AND
|
|
87
|
+
- `table_info` present
|
|
88
|
+
|
|
89
|
+
Implementation entry points:
|
|
90
|
+
- `Elke27Client.async_connect(host, port, link_keys)`
|
|
91
|
+
- `Elke27Client.wait_ready(timeout_s=...)`
|
|
92
|
+
|
|
93
|
+
### 4) Features (domain APIs)
|
|
94
|
+
The library exposes “feature” modules (domains) that implement E27 API handlers such as:
|
|
95
|
+
- areas, zones, outputs, thermostats, keypads, control/system, network parameters, rules, logs, users, etc.
|
|
96
|
+
|
|
97
|
+
These are internal to the library; Home Assistant should not import them directly and should rely on the client contract and snapshots/events.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Installation / Development setup
|
|
102
|
+
|
|
103
|
+
The project is packaged via `pyproject.toml` and requires Python >=3.11.
|
|
104
|
+
|
|
105
|
+
Typical development setup (example):
|
|
106
|
+
- create a virtual environment
|
|
107
|
+
- install with development extras
|
|
108
|
+
- run pytest
|
|
109
|
+
|
|
110
|
+
(Exact tooling choice is up to you; the repository includes standard pyproject-based metadata and pytest configuration.)
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Example programs
|
|
115
|
+
|
|
116
|
+
Examples live under `examples/` and are intended to be runnable against a real panel on your LAN.
|
|
117
|
+
|
|
118
|
+
### Environment setup (required)
|
|
119
|
+
|
|
120
|
+
Before running any example programs **or live tests**, you should:
|
|
121
|
+
|
|
122
|
+
1. Edit the file:
|
|
123
|
+
- `elk-e27-env-vars.sh`
|
|
124
|
+
|
|
125
|
+
2. Set the appropriate values for your environment, such as:
|
|
126
|
+
- panel host / port
|
|
127
|
+
- access code
|
|
128
|
+
- passphrase
|
|
129
|
+
- optional identity fields
|
|
130
|
+
|
|
131
|
+
3. Source the file into your shell:
|
|
132
|
+
- `source elk-e27-env-vars.sh`
|
|
133
|
+
|
|
134
|
+
This ensures all required environment variables are defined consistently for
|
|
135
|
+
example programs and pytest live tests.
|
|
136
|
+
|
|
137
|
+
### 1) Discover panels (no linking, no connect)
|
|
138
|
+
- `examples/e27_aioscanner.py`
|
|
139
|
+
- Scans the LAN and prints discovered panels.
|
|
140
|
+
|
|
141
|
+
### 2) Provisioning (link only)
|
|
142
|
+
- `examples/e27_api_link.py`
|
|
143
|
+
- Performs API_LINK and prints the resulting link credentials.
|
|
144
|
+
- Does not start a full session or send operational requests.
|
|
145
|
+
|
|
146
|
+
### 3) Minimal “hello world” connect + version info
|
|
147
|
+
- `examples/e27_simple_program.py`
|
|
148
|
+
- Discover → link → connect → request `control.get_version_info` → pump events until results arrive.
|
|
149
|
+
|
|
150
|
+
### 4) Monitor unsolicited messages/events
|
|
151
|
+
- `examples/e27_unsolicited_monitor.py`
|
|
152
|
+
- Link → connect → print unsolicited messages/events.
|
|
153
|
+
|
|
154
|
+
### 5) Client contract examples (Home Assistant–style usage)
|
|
155
|
+
- `examples/e27_client_contract.py`
|
|
156
|
+
- `examples/e27_client_contract_example.py`
|
|
157
|
+
- Demonstrate the “stable client contract” flow:
|
|
158
|
+
- create client → connect → wait_ready → subscribe → receive event → read a snapshot
|
|
159
|
+
|
|
160
|
+
### 6) Network parameter example with authorization retry
|
|
161
|
+
- `examples/e27_network_param_live.py`
|
|
162
|
+
- Demonstrates a privileged call path that may require PIN authorization.
|
|
163
|
+
|
|
164
|
+
### 7) Live version info printout
|
|
165
|
+
- `examples/e27_live_version_info.py`
|
|
166
|
+
- End-to-end link/connect/version info with PanelState printing.
|
|
167
|
+
|
|
168
|
+
### Common environment variables used by examples
|
|
169
|
+
Examples rely on environment variables typically defined via
|
|
170
|
+
`elk-e27-env-vars.sh`, including:
|
|
171
|
+
- `ELKE27_HOST`, `ELKE27_PORT`
|
|
172
|
+
- `ELKE27_ACCESS_CODE`, `ELKE27_PASSPHRASE`
|
|
173
|
+
- Identity fields (optional): `ELKE27_MN`, `ELKE27_SN`, `ELKE27_FWVER`,
|
|
174
|
+
`ELKE27_HWVER`, `ELKE27_OSVER`
|
|
175
|
+
|
|
176
|
+
Many examples also accept command-line arguments (for example, `--host`,
|
|
177
|
+
`--port`) which override environment defaults.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Tests
|
|
182
|
+
|
|
183
|
+
Tests live under `test/` and are split into:
|
|
184
|
+
- **unit tests** (run without a live panel)
|
|
185
|
+
- **live tests** (require a real E27 panel and credentials)
|
|
186
|
+
|
|
187
|
+
### Running unit tests
|
|
188
|
+
Unit tests should run with a normal pytest invocation.
|
|
189
|
+
|
|
190
|
+
### Running live tests
|
|
191
|
+
Live tests are gated and will skip unless enabled. In addition to sourcing
|
|
192
|
+
`elk-e27-env-vars.sh`, live tests must be explicitly enabled via environment
|
|
193
|
+
configuration.
|
|
194
|
+
|
|
195
|
+
If required environment values are missing, tests are skipped with a message
|
|
196
|
+
indicating what is needed.
|
|
197
|
+
|
|
198
|
+
### Test artifacts / reporting
|
|
199
|
+
The test harness supports writing artifacts for each pytest invocation, typically
|
|
200
|
+
in JSONL and optionally YAML.
|
|
201
|
+
|
|
202
|
+
Relevant pytest options:
|
|
203
|
+
- `--e27-report` : `jsonl` (default), `yaml`, `both`, or `none`
|
|
204
|
+
- `--e27-artifacts-dir` : output directory (default: `artifacts/test_runs`)
|
|
205
|
+
|
|
206
|
+
You can also set:
|
|
207
|
+
- `ELK_E27_REPORT_FORMAT`
|
|
208
|
+
- `ELK_E27_ARTIFACTS_DIR`
|
|
209
|
+
|
|
210
|
+
Artifacts are designed to support debugging and regression tracking across
|
|
211
|
+
protocol changes.
|
|
212
|
+
|
|
213
|
+
### Notable test coverage areas
|
|
214
|
+
The test suite includes coverage for:
|
|
215
|
+
- framing and presentation layer behavior
|
|
216
|
+
- sequence handling and request/response correlation
|
|
217
|
+
- bootstrap readiness
|
|
218
|
+
- configured inventory enumeration
|
|
219
|
+
- paging behavior and dispatcher correctness
|
|
220
|
+
- keepalive and reconnect-adjacent behaviors
|
|
221
|
+
- permissions and authorization flows (including live tests)
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Documentation
|
|
226
|
+
|
|
227
|
+
Key documents:
|
|
228
|
+
- `docs/CLIENT_CONTRACT.md` — the stable Home Assistant–facing API contract
|
|
229
|
+
- `docs/PERMISSIONS.md` — permission and authorization semantics
|
|
230
|
+
- `docs/adr/` — architecture decision records guiding behavior and boundaries
|
|
231
|
+
|
|
232
|
+
If you are working on a Home Assistant integration:
|
|
233
|
+
- follow the import boundary rules in `CLIENT_CONTRACT.md`
|
|
234
|
+
- prefer consuming snapshots and events rather than internal protocol details
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Notes for Home Assistant integration authors
|
|
239
|
+
|
|
240
|
+
- This library is event-driven and designed for a “hub” style Home Assistant
|
|
241
|
+
integration.
|
|
242
|
+
- Linking keys are long-lived and should be persisted by Home Assistant.
|
|
243
|
+
- Session keys and `session_id` are ephemeral and should not be persisted.
|
|
244
|
+
- Privileged authorization is temporary (vendor-defined behavior); Home Assistant
|
|
245
|
+
should be prepared to re-authorize as needed and should not assume privilege
|
|
246
|
+
persists indefinitely.
|
|
247
|
+
- Request concurrency should remain conservative (one request at a time per
|
|
248
|
+
panel session) to avoid exhausting panel resources.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Repository layout (high level)
|
|
253
|
+
|
|
254
|
+
- `src/elke27_lib/` — library implementation
|
|
255
|
+
- `docs/` — client contract, permissions, and ADRs
|
|
256
|
+
- `examples/` — runnable sample programs
|
|
257
|
+
- `test/` — pytest suite (unit + live)
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Support / Contributing
|
|
262
|
+
|
|
263
|
+
- Keep Home Assistant–facing changes aligned with `docs/CLIENT_CONTRACT.md`.
|
|
264
|
+
- Prefer small, testable changes.
|
|
265
|
+
- When adding new feature coverage, consider:
|
|
266
|
+
- unit tests for parsing, dispatch, and mapping
|
|
267
|
+
- live tests for end-to-end behavior (when hardware is available)
|
|
268
|
+
- updating documentation or ADRs if behavior changes
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
* * *
|
|
273
|
+
|
|
274
|
+
## Project Docs
|
|
275
|
+
|
|
276
|
+
For how to install uv and Python, see [installation.md](docs/installation.md).
|
|
277
|
+
|
|
278
|
+
For development workflows, see [development.md](docs/development.md).
|
|
279
|
+
|
|
280
|
+
For instructions on publishing to PyPI, see [publishing.md](docs/publishing.md).
|
|
281
|
+
|
|
282
|
+
* * *
|
|
283
|
+
|
|
284
|
+
*This project was built from
|
|
285
|
+
[simple-modern-uv](https://github.com/jlevy/simple-modern-uv).*
|