Skip to content

Technical Design Document: Crow AAP IP Module Integration

acdcnow edited this page Sep 21, 2026 · 2 revisions

ARCHIVED - historical reference only. This page documents a superseded version of the integration and is no longer maintained. For current documentation see the Home page and the HA 2026.09 Development Branch page. Full index: Archived Documentation. Here is the comprehensive Technical Design & Function Documentation for the Crow/AAP IP Module Integration. This document serves as a reference for developers and advanced users to understand the internal architecture, design decisions, and function logic.


Technical Design Document: Crow/AAP IP Module Integration

1. Architectural Overview

This integration follows the Local Push architecture. It establishes a persistent TCP socket connection to the alarm hardware using the pycrowipmodule library.

Data Flow Diagram

  1. Hardware: The Crow IP Module sends ASCII strings over TCP.
  2. Library (pycrowipmodule): Runs in a background thread, parses the ASCII strings into a state dictionary (system_state, zone_state, area_state).
  3. Callbacks: When state changes, the library triggers a callback function defined in __init__.py.
  4. Thread Bridge: Since the library callback runs in a worker thread, hass.loop.call_soon_threadsafe is used to marshall the data back to the main Home Assistant Event Loop.
  5. Dispatcher: The async_dispatcher_send signal broadcasts the data.
  6. Entities: Individual entities (Sensors, Alarm Panel) listen to these signals and update their state in Home Assistant.

2. Core Components & Logic

A. Configuration Flow (config_flow.py)

Design Pattern: Multi-step Wizard with State Persistence.

Instead of a single large form, the setup is split into 4 steps to handle the complexity of configuring 16 zones and multiple partitions.

  • CrowConfigFlow Class:
  • async_step_user: Entry point. Immediately redirects to the first configuration step.
  • async_step_areas: Collects names and codes for Area A and Area B. Stores them in self.areas_config.
  • async_step_outputs: Collects names for Output 3 and 4 (the switchable outputs). Stores in self.outputs_config.
  • async_step_zones: A loop generates 16 fields for naming. Uses vol.In(ZONE_TYPES) to render a Dropdown menu for Zone Types. Stores in self.zones_config.
  • async_step_connection: Collects IP, Port, and Timeout. This is the final step where:
  1. Data from all previous steps is merged.
  2. CONF_AREAS, CONF_ZONES, and CONF_OUTPUTS are stored in the options dictionary of the config entry (making them mutable).
  3. Connection details (CONF_HOST) are stored in the data dictionary (immutable).
  4. Uniqueness is verified (_abort_if_unique_id_configured).
  • CrowOptionsFlowHandler Class:
  • Allows re-configuration of names and types after installation.
  • Robustness Logic: Uses .get(key) or {} extensively to prevent 500 Internal Server Errors if the existing configuration is incomplete or corrupted.

B. Initialization & Thread Safety (__init__.py)

Design Pattern: Async Wrapper & Signal Dispatching.

This file bridges the synchronous/threaded nature of the external library with the asynchronous nature of Home Assistant.

  • async_setup_entry(hass, entry):
  1. Initializes the CrowIPAlarmPanel controller.
  2. The Thread-Safe Wrapper (_thread_safe_send):
  • Problem: The Crow library fires callbacks from a background thread. Calling HA functions directly causes Fatal error: protocol.data_received().
  • Solution: ```python def _thread_safe_send(signal, data): hass.loop.call_soon_threadsafe(async_dispatcher_send, hass, signal, data)
This forces the execution onto the main HA event loop.

  1. Non-Blocking Start:
  • Problem: Connecting to the alarm might take time or timeout (60s+), causing Bootstrap stage 2 timeout.
  • Solution: hass.async_add_executor_job(controller.start) is called without await. This fires the connection process in the background, allowing HA to finish booting immediately.

C. Alarm Control Panel (alarm_control_panel.py)

Design Pattern: State Machine & Command Sequencing.

  • CrowAlarmPanel Class:

  • Keypad Logic:

  • code_format returns CodeFormat.NUMBER always, ensuring the keypad is visible.

  • code_arm_required returns False if a default code is configured. This creates a hybrid mode: The user can type a code, but doesn't have to.

  • Arming Logic (async_alarm_arm_...):

  • Crow systems require a specific sequence: Send Command (arm_stay) -> Send Code (keypress).

  • The function splits this into two network calls.

  • State Mapping: Maps the raw boolean flags from the library (armed, stay_armed, exit_delay) to HA enums (AlarmControlPanelState.ARMED_AWAY, etc.).

D. Binary Sensors (binary_sensor.py)

Design Pattern: Inheritance & Logic Inversion.

  • CrowBaseEntity Class:

  • Base class for all binary sensors.

  • Handles device_info to link entities to the device registry.

  • Stores self._host locally to avoid AttributeError if the controller object doesn't expose the IP.

  • CrowSystemStatusSensor Class (Diagnostic):

  • Handles system flags like Mains, Battery, Tamper.

  • Logic Inversion:

  • Battery: Library sends True for OK. HA expects On for Problem. Logic: return not val.

  • Tamper: Library sends True for Closed. HA expects On for Open. Logic: return not val.

  • Mains: Library sends True for OK. HA expects On for Power Present. Logic: return val.

  • Categorized as EntityCategory.DIAGNOSTIC so they appear in a separate section in the UI.

E. Switches & Outputs (switch.py)

Design Pattern: Optimistic Updates.

  • CrowOutput: Represents the switchable outputs (3 & 4).

  • Uses optimistic state updating (self.async_write_ha_state()) immediately after sending a command to provide instant UI feedback, while waiting for the real confirmation callback from the panel.

  • CrowRelay: Represents hardware relays (1 & 2).

  • Modeled as momentary switches (State is always False). Turning them "On" sends a pulse and immediately resets.


3. Data Structures

Configuration Entry Options

The configuration is stored in JSON format within Home Assistant's storage (.storage/core.config_entries).

{
  "options": {
    "areas": {
      "1": { "name": "House", "code": "1234", "code_arm_required": true },
      "2": { "name": "Garage", "code": "", "code_arm_required": true }
    },
    "zones": {
      "1": { "name": "Front Door", "type": "door" },
      "2": { "name": "Living Room", "type": "motion" }
      // ... up to 16
    },
    "outputs": {
      "3": { "name": "Gate" },
      "4": { "name": "Light" }
    }
  }
}

Signal Map

These strings are used by the async_dispatcher_send to route data to the correct entities:

Signal String Payload Data Listener
crowipmodule.zones_updated Zone ID Updates binary_sensor (Zones)
crowipmodule.areas_updated Area ID Updates alarm_control_panel
crowipmodule.system_updated Full State Dict Updates sensor (Status) & binary_sensor (Diagnostic)
crowipmodule.output_updated Output ID Updates switch

4. Dependencies

  • Library: pycrowipmodule >= 0.32
  • Protocol: ASCII based Telnet-style communication.
  • Home Assistant APIs: config_entries, core, helpers.dispatcher, helpers.entity, voluptuous.

Clone this wiki locally