Skip to content

feat: type-checking#312

Open
GuyKh wants to merge 1 commit intomainfrom
type-fixing
Open

feat: type-checking#312
GuyKh wants to merge 1 commit intomainfrom
type-fixing

Conversation

@GuyKh
Copy link
Owner

@GuyKh GuyKh commented Feb 10, 2026

PR Type

Enhancement, Tests


Description

  • Add comprehensive type annotations throughout codebase for MyPy compatibility

  • Implement MyPy static type checking with strict configuration

  • Add GitHub Actions workflow for automated type checking on PRs

  • Set up pre-commit hooks for local linting and type validation

  • Fix type-related issues in coordinator, sensors, and config flow modules


File Walkthrough

Relevant files
Type checking
7 files
__init__.py
Add type hints to async functions and imports                       
+4/-2     
binary_sensor.py
Add type annotations to callable and dict parameters         
+9/-7     
commons.py
Fix import order and add explicit bool return type             
+5/-4     
config_flow.py
Fix ConfigFlow domain declaration and add type hints         
+19/-17 
coordinator.py
Add comprehensive type hints to all methods and attributes
+94/-92 
iec_entity.py
Fix relative imports and add return type hints                     
+3/-3     
sensor.py
Add type annotations to callable types and attributes       
+12/-15 
Configuration changes
2 files
lint.yml
Add MyPy type checking job and auto-fix workflow                 
+62/-1   
mypy.ini
Create MyPy configuration with strict type checking rules
+66/-0   
Documentation
1 files
CONTRIBUTING.md
Update guidelines with type checking and setup instructions
+41/-23 
Dependencies
1 files
requirements.txt
Add MyPy and pytz type stubs dependencies                               
+2/-0     
Enhancement
3 files
lint
Add MyPy execution to linting script                                         
+5/-0     
setup
Add pre-commit hook setup for linting and type checking   
+87/-0   
typecheck
Create dedicated type checking script                                       
+8/-0     

@qodo-code-review
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
CI auto-commit risk

Description: The new autofix GitHub Actions job runs on pull_request with contents: write permission
and automatically commits and pushes changes back to the PR branch, which can enable
unreviewed CI-driven modifications to be written to the repository/branch (e.g., if the
workflow is triggered on a branch within the main repo or if permissions are mis-scoped).
lint.yml [50-90]

Referred Code
autofix:
  name: "Auto-fix"
  runs-on: "ubuntu-latest"
  if: github.event_name == 'pull_request'
  permissions:
    contents: write
  steps:
      - name: "Checkout the repository"
        uses: "actions/checkout@v6.0.2"
        with:
          ref: ${{ github.head_ref }}

      - name: "Set up Python"
        uses: actions/setup-python@v6.2.0
        with:
          python-version: "3.11"
          cache: "pip"

      - name: "Install requirements"
        run: python3 -m pip install -r requirements.txt



 ... (clipped 20 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Sensitive data logged: The new debug handler logs iec_coordinator.data verbatim which can contain sensitive
information (e.g., tokens/JWTs, identifiers), violating secure logging requirements.

Referred Code
async def handle_debug_get_coordinator_data(call: Any) -> None:
    # Log or return coordinator data
    data = iec_coordinator.data
    _LOGGER.info("Coordinator data: %s", data)
    hass.bus.async_fire("custom_component_debug_event", {"data": data})

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unsanitized data sharing: The debug service emits raw coordinator data on the event bus via hass.bus.async_fire,
which may disclose secrets/PII to other listeners without sanitization or access control.

Referred Code
async def handle_debug_get_coordinator_data(call: Any) -> None:
    # Log or return coordinator data
    data = iec_coordinator.data
    _LOGGER.info("Coordinator data: %s", data)
    hass.bus.async_fire("custom_component_debug_event", {"data": data})

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Debug logging event: The newly added debug service logs and emits raw coordinator data without any audit
context (user ID, outcome, purpose) and may undermine reliable audit reconstruction
depending on how the service is used.

Referred Code
async def handle_debug_get_coordinator_data(call: Any) -> None:
    # Log or return coordinator data
    data = iec_coordinator.data
    _LOGGER.info("Coordinator data: %s", data)
    hass.bus.async_fire("custom_component_debug_event", {"data": data})

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Debug data exposure: The new debug service can expose internal state (iec_coordinator.data) through
logs/events, which may effectively surface sensitive internal details to end users
depending on Home Assistant log/event access.

Referred Code
async def handle_debug_get_coordinator_data(call: Any) -> None:
    # Log or return coordinator data
    data = iec_coordinator.data
    _LOGGER.info("Coordinator data: %s", data)
    hass.bus.async_fire("custom_component_debug_event", {"data": data})

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Use correct domain attribute

Rename the class attribute DOMAIN to domain in IecConfigFlow to comply with Home
Assistant's requirements for config flow registration.

custom_components/iec/config_flow.py [77-81]

 class IecConfigFlow(config_entries.ConfigFlow):
     """Handle a config flow for IEC."""
 
     VERSION = 1
-    DOMAIN = DOMAIN
+    domain = DOMAIN
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical issue where the config flow would fail to register due to an incorrect class attribute name (DOMAIN instead of domain). This change is essential for the integration's core functionality.

High
Add a null check before access

Add a null check for the account variable before accessing its id attribute to
prevent a potential AttributeError.

custom_components/iec/coordinator.py [336-343]

 async def _get_account_id(self) -> str | None:
     if not self._account_id:
         try:
             account = await self.api.get_default_account()
-            self._account_id = str(account.id) if account.id else None
+            if account:
+                self._account_id = str(account.id) if account.id else None
         except IECError as e:
             _LOGGER.exception("Failed fetching Account", e)
     return self._account_id
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential AttributeError if self.api.get_default_account() returns None. The proposed change prevents a runtime crash, making the code more robust.

Medium
Use timezone-aware datetime

Make the datetime object passed to _get_readings timezone-aware by using
TIMEZONE.localize() to ensure consistency.

custom_components/iec/coordinator.py [643-651]

+# build a timezone-aware datetime before the call
+reading_dt = (
+    TIMEZONE.localize(datetime.combine(reading_date, datetime.min.time()))
+    if reading_date
+    else localized_today
+)
 remote_reading = await self._get_readings(
     contract_id,
     device.device_number,
     device.device_code,
-    datetime.combine(reading_date, datetime.min.time())
-    if reading_date
-    else datetime.now(),
+    reading_dt,
     reading_type,
 )

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that a naive datetime object is being passed to _get_readings. Using a timezone-aware datetime object would improve consistency and prevent potential issues related to timezones, which is a good practice.

Medium
  • More

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant