Skip to content

Refactor: Cleanup legacy MQTT, username, and AGENT_USER_ID#131

Merged
DaTiC0 merged 2 commits into
mainfrom
cleanup-legacy
May 5, 2026
Merged

Refactor: Cleanup legacy MQTT, username, and AGENT_USER_ID#131
DaTiC0 merged 2 commits into
mainfrom
cleanup-legacy

Conversation

@DaTiC0

@DaTiC0 DaTiC0 commented May 5, 2026

Copy link
Copy Markdown
Owner

Overview

This PR removes legacy components identified during the cleanup phase to improve codebase maintainability and ensure strict multi-tenant isolation.

Changes:

  • MQTT: Removed legacy subscriptions (+/notification, +/status) in favor of user-separated topics.
  • Models: Removed the deprecated username column from the User model.
  • Configuration: Removed AGENT_USER_ID fallback logic and logging.
  • UI/API: Updated OAuth authorization and /api/me to use email as the primary identifier.
  • Cleanup: Removed irrelevant/confusing comments in action_devices.py.
  • Testing: Updated the test suite to reflect these changes.

Verification:

  • Ran make test: All 43 tests passed.

Summary by Sourcery

Remove legacy single-tenant fallbacks and identifiers in favor of user-scoped, email-based, multi-tenant behavior.

Enhancements:

  • Simplify smart home user resolution and sync/report_state behavior by eliminating AGENT_USER_ID-based fallbacks and using explicit user scope only.
  • Switch OAuth /api/me response and authorization template to use email as the primary user identifier instead of username.
  • Remove legacy MQTT topic subscriptions tied to non–user-scoped topics and rely solely on multi-tenant topic structure.
  • Drop the deprecated username field from the User model and related test/fixture usage, standardizing on email.

Documentation:

  • Update README to remove the deprecated AGENT_USER_ID configuration option.

Tests:

  • Update OAuth and multi-tenant tests to reflect email-based identity and the removal of AGENT_USER_ID fallback behavior.

Chores:

  • Clean up confusing comments and redundant logging related to legacy agent user handling.

@sourcery-ai

sourcery-ai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Refactors legacy user-identification and MQTT behavior to remove AGENT_USER_ID and username usage, enforces email-based identity and strict multi-tenant behavior, and updates tests, templates, and docs accordingly.

Sequence diagram for onSync user resolution without AGENT_USER_ID fallback

sequenceDiagram
    actor Client
    participant Routes as Routes_bp
    participant ActionDevices as ActionDevices_onSync
    participant UserScope as NormalizeUserScope

    Client->>Routes: POST /sync
    Routes->>UserScope: _resolve_smarthome_user_scope
    UserScope-->>Routes: user_id None when no session
    Routes->>ActionDevices: onSync(user_id None, agent_user_id None)

    ActionDevices->>UserScope: _normalize_user_scope(user_id)
    UserScope-->>ActionDevices: resolved_user None

    ActionDevices->>ActionDevices: agent_user = str(agent_user_id or resolved_user)
    ActionDevices-->>Client: agentUserId "unknown" on error

    note over ActionDevices,Routes: Config AGENT_USER_ID is not used as fallback anymore
Loading

ER diagram for updated User table without username

erDiagram
    USER {
        int id PK
        varchar email
        varchar password
    }

    OAUTH_TOKEN {
        int id PK
        int user_id FK
        varchar access_token
        varchar refresh_token
    }

    USER ||--o{ OAUTH_TOKEN : has
Loading

Class diagram for updated user and configuration models

classDiagram
    class User {
        +Integer id
        +String email
        +String password
    }

    class Config {
        +Bool MQTT_TLS_ENABLED
        +Int MQTT_TLS_VERSION
        +String API_KEY
        +String DATABASEURL
    }

    class OAuthToken {
        +Integer id
        +Integer user_id
        +String access_token
        +String refresh_token
    }

    User "1" -- "many" OAuthToken : owns
Loading

File-Level Changes

Change Details Files
Remove AGENT_USER_ID-based fallback behavior in smart home handlers in favor of explicit user scope or an "unknown" placeholder.
  • Update onSync to derive agentUserId from provided agent_user_id or resolved user only, no config fallback
  • Change onSync error path to return agentUserId="unknown" instead of config-based fallback
  • Update report_state to use provided user_id or "unknown" instead of AGENT_USER_ID config
action_devices.py
Tighten smart home user scope resolution to avoid implicit fallbacks and rely solely on explicit request or session context.
  • Change _resolve_smarthome_user_scope to drop AGENT_USER_ID-based fallback and return (None, None) when no scope found
  • Update function docstring to remove reference to AGENT_USER_ID fallback
routes.py
Standardize OAuth identity on email instead of username across API and UI.
  • Change /api/me endpoint to return email field instead of username
  • Update authorization template to display user.email instead of user.username
routes.py
templates/authorize.html
Remove legacy AGENT_USER_ID configuration and logging and clean up legacy MQTT topics.
  • Remove AGENT_USER_ID from logging in app startup
  • Remove AGENT_USER_ID configuration entry
  • Update Firebase/MQTT initialization to drop legacy single-level MQTT subscriptions and rely only on user-separated topics
  • Remove AGENT_USER_ID from README environment variable list
app.py
config.py
README.md
Remove deprecated username field from User model and migrate tests to email-based assertions, dropping AGENT_USER_ID dependencies.
  • Delete username column from User model
  • Update OAuth test user fixture to no longer set username
  • Adjust tests to assert on email in /api/me response
  • Remove AGENT_USER_ID setup from multi-tenant tests
models.py
tests/test_app.py
tests/test_multi_tenant.py
Clarify command handler purpose by replacing confusing comment with a descriptive docstring.
  • Replace vague and misspelled docstring in commands() with a concise description of its responsibilities
action_devices.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • You now return (None, None) from _resolve_smarthome_user_scope where a concrete fallback was previously guaranteed; consider double-checking all call sites to avoid accidentally propagating None into places that expect a non-empty user_id or agent_user_id string.
  • The literal string "unknown" is now used in multiple places for agentUserId; consider centralizing this into a constant or helper to keep behavior consistent and easier to change later.
  • In onSync, when both resolved_user and agent_user_id are None, agent_user becomes the string 'None' whereas error handling returns 'unknown'; you might want to align these code paths to avoid inconsistent identifiers for the same situation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- You now return `(None, None)` from `_resolve_smarthome_user_scope` where a concrete fallback was previously guaranteed; consider double-checking all call sites to avoid accidentally propagating `None` into places that expect a non-empty `user_id` or `agent_user_id` string.
- The literal string "unknown" is now used in multiple places for `agentUserId`; consider centralizing this into a constant or helper to keep behavior consistent and easier to change later.
- In `onSync`, when both `resolved_user` and `agent_user_id` are `None`, `agent_user` becomes the string `'None'` whereas error handling returns `'unknown'`; you might want to align these code paths to avoid inconsistent identifiers for the same situation.

## Individual Comments

### Comment 1
<location path="action_devices.py" line_range="184" />
<code_context>
         resolved_user = _normalize_user_scope(user_id)
-        fallback = resolved_user or current_app.config.get('AGENT_USER_ID', 'test-user')
-        agent_user = str(agent_user_id if agent_user_id is not None else fallback)
+        agent_user = str(agent_user_id if agent_user_id is not None else resolved_user)
         return {
             "agentUserId": agent_user,
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid returning the literal string 'None' as agentUserId when no user is resolved.

With this change, if both `agent_user_id` and `resolved_user` are `None`, `agent_user` becomes the literal string `'None'`. That’s inconsistent with the error path (which uses `'unknown'`) and may confuse downstream consumers. Consider falling back to a stable value like `'unknown'` here (e.g. `resolved_user or 'unknown'`).
</issue_to_address>

### Comment 2
<location path="tests/test_app.py" line_range="304-306" />
<code_context>
         self.assertEqual(me_resp.status_code, 200)
         me_payload = me_resp.get_json()
         self.assertIsInstance(me_payload, dict)
-        self.assertEqual(me_payload.get('username'), 'oauth-user')
+        self.assertEqual(me_payload.get('email'), 'oauth@example.com')

     def test_authorize_deny_consent_redirects_with_access_denied(self):
</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen the `/me` endpoint test to assert the full response shape, not just the email value.

Instead of checking only the `email` field, assert the full payload matches the expected schema, e.g. `self.assertEqual(me_payload, {"email": "oauth@example.com"})`. This will ensure `username` is absent and prevent unnoticed additions of unexpected fields in future changes.

```suggestion
        self.assertEqual(me_resp.status_code, 200)
        me_payload = me_resp.get_json()
        self.assertIsInstance(me_payload, dict)
        self.assertEqual(me_payload, {"email": "oauth@example.com"})
```
</issue_to_address>

### Comment 3
<location path="tests/test_multi_tenant.py" line_range="17" />
<code_context>
 class MultiTenantTest(unittest.TestCase):
     def setUp(self):
         self.app = Flask(__name__)
-        self.app.config['AGENT_USER_ID'] = 'test-fallback-user'
         self.ctx = self.app.app_context()
         self.ctx.push()
</code_context>
<issue_to_address>
**suggestion (testing):** Add explicit tests for behavior when no `AGENT_USER_ID` fallback is configured.

Since `_resolve_smarthome_user_scope` no longer uses `AGENT_USER_ID`, multi-tenant behavior now depends only on the request `agentUserId` and the authenticated user. Please add tests for the "no fallback" scenarios (e.g., no `agentUserId` and no authenticated user) across sync/query/execute flows and any helpers that resolve user scope, to lock in and document the new default behavior and prevent regressions if a fallback is reintroduced.

Suggested implementation:

```python
import os
os.environ['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
os.environ['APP_ENV'] = 'testing'

import unittest
from flask import Flask

from action_devices import onSync, actions
from notifications import handle_messages, get_mqtt_logs, _append_mqtt_log


class MultiTenantTest(unittest.TestCase):
    def setUp(self):
        self.app = Flask(__name__)
        # Explicitly ensure no AGENT_USER_ID fallback is configured for these tests
        self.app.config.pop('AGENT_USER_ID', None)
        self.ctx = self.app.app_context()
        self.ctx.push()

    @unittest.skip("Define expectations for sync behavior when no agentUserId and no authenticated user, then remove this skip.")
    def test_sync_without_fallback_no_agent_or_user(self):
        """Explicitly document behavior when there is no AGENT_USER_ID, no agentUserId, and no authenticated user on SYNC."""
        # TODO: Construct a SYNC request with no agentUserId and no authenticated user context
        # and assert the resulting behavior (e.g., error, empty device list, etc.)
        self.fail("Implement SYNC no-fallback/no-user behavior assertion")

    @unittest.skip("Define expectations for query behavior when no agentUserId and no authenticated user, then remove this skip.")
    def test_query_without_fallback_no_agent_or_user(self):
        """Explicitly document behavior when there is no AGENT_USER_ID, no agentUserId, and no authenticated user on QUERY."""
        # TODO: Construct a QUERY request with no agentUserId and no authenticated user context
        # and assert the resulting behavior (e.g., error response, empty states, etc.)
        self.fail("Implement QUERY no-fallback/no-user behavior assertion")

    @unittest.skip("Define expectations for execute behavior when no agentUserId and no authenticated user, then remove this skip.")
    def test_execute_without_fallback_no_agent_or_user(self):
        """Explicitly document behavior when there is no AGENT_USER_ID, no agentUserId, and no authenticated user on EXECUTE."""
        # TODO: Construct an EXECUTE request with no agentUserId and no authenticated user context
        # and assert the resulting behavior (e.g., error response, no side effects, etc.)
        self.fail("Implement EXECUTE no-fallback/no-user behavior assertion")

    @unittest.skip("Wire this test to the helper that resolves user scope and assert its behavior with no fallback.")
    def test_user_scope_resolution_without_fallback(self):
        """Lock in behavior of whatever helper resolves smarthome user scope when no AGENT_USER_ID, no agentUserId, and no authenticated user."""
        # TODO: Call the helper used by SYNC/QUERY/EXECUTE to resolve user scope and
        # assert the expected behavior (e.g., returns None/raises, etc.) in the no-fallback case.
        self.fail("Implement user-scope helper no-fallback/no-user behavior assertion")

```

To fully implement the requested tests and remove the `@unittest.skip` decorators and `self.fail` placeholders, you will need to:

1. Mirror the patterns used in your existing multi-tenant tests (likely in this same file) for constructing SYNC/QUERY/EXECUTE requests:
   - Reuse the same helpers or factories that build request payloads and contexts.
   - Ensure *no* `agentUserId` is included in the request payload.
   - Ensure there is *no* authenticated user attached (e.g., no session, no JWT, no user object in the context).

2. For `test_sync_without_fallback_no_agent_or_user`:
   - Call into the same entry point your existing SYNC tests use (e.g., a Flask test client hitting the SYNC endpoint or direct invocation of `onSync`/`actions.onSync` with the appropriate arguments).
   - Assert the exact expected behavior in the no-fallback/no-user case (for example: HTTP 401/403, specific error payload, or an empty device list), and replace the `self.fail` with those assertions.

3. For `test_query_without_fallback_no_agent_or_user` and `test_execute_without_fallback_no_agent_or_user`:
   - Use the same entry points used by your current QUERY/EXECUTE tests (e.g., `actions.onQuery`, `actions.onExecute`, or HTTP endpoints).
   - Assert the exact expected behavior (e.g., error code, empty response, or specific error structure) when no user scope can be resolved.

4. For `test_user_scope_resolution_without_fallback`:
   - Import and call the helper that replaced `_resolve_smarthome_user_scope` (or `_resolve_smarthome_user_scope` if it still exists) that is responsible for resolving the user scope used by SYNC/QUERY/EXECUTE.
   - Construct its inputs to represent "no `agentUserId` and no authenticated user" and assert its behavior (e.g., returns `None`, raises a specific exception, or produces a special sentinel).
   - Update the test body to call this helper and assert on its return/exception instead of `self.fail`.

Once you wire these tests to the actual request/handler/helper APIs and define the assertions that match your intended "no fallback" default behavior, you can remove the `@unittest.skip` decorators so these tests actively lock in that behavior and prevent regressions if a fallback is reintroduced.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread action_devices.py Outdated
Comment thread tests/test_app.py
Comment on lines 304 to 306
self.assertEqual(me_resp.status_code, 200)
me_payload = me_resp.get_json()
self.assertIsInstance(me_payload, dict)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Strengthen the /me endpoint test to assert the full response shape, not just the email value.

Instead of checking only the email field, assert the full payload matches the expected schema, e.g. self.assertEqual(me_payload, {"email": "oauth@example.com"}). This will ensure username is absent and prevent unnoticed additions of unexpected fields in future changes.

Suggested change
self.assertEqual(me_resp.status_code, 200)
me_payload = me_resp.get_json()
self.assertIsInstance(me_payload, dict)
self.assertEqual(me_resp.status_code, 200)
me_payload = me_resp.get_json()
self.assertIsInstance(me_payload, dict)
self.assertEqual(me_payload, {"email": "oauth@example.com"})

class MultiTenantTest(unittest.TestCase):
def setUp(self):
self.app = Flask(__name__)
self.app.config['AGENT_USER_ID'] = 'test-fallback-user'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Add explicit tests for behavior when no AGENT_USER_ID fallback is configured.

Since _resolve_smarthome_user_scope no longer uses AGENT_USER_ID, multi-tenant behavior now depends only on the request agentUserId and the authenticated user. Please add tests for the "no fallback" scenarios (e.g., no agentUserId and no authenticated user) across sync/query/execute flows and any helpers that resolve user scope, to lock in and document the new default behavior and prevent regressions if a fallback is reintroduced.

Suggested implementation:

import os
os.environ['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
os.environ['APP_ENV'] = 'testing'

import unittest
from flask import Flask

from action_devices import onSync, actions
from notifications import handle_messages, get_mqtt_logs, _append_mqtt_log


class MultiTenantTest(unittest.TestCase):
    def setUp(self):
        self.app = Flask(__name__)
        # Explicitly ensure no AGENT_USER_ID fallback is configured for these tests
        self.app.config.pop('AGENT_USER_ID', None)
        self.ctx = self.app.app_context()
        self.ctx.push()

    @unittest.skip("Define expectations for sync behavior when no agentUserId and no authenticated user, then remove this skip.")
    def test_sync_without_fallback_no_agent_or_user(self):
        """Explicitly document behavior when there is no AGENT_USER_ID, no agentUserId, and no authenticated user on SYNC."""
        # TODO: Construct a SYNC request with no agentUserId and no authenticated user context
        # and assert the resulting behavior (e.g., error, empty device list, etc.)
        self.fail("Implement SYNC no-fallback/no-user behavior assertion")

    @unittest.skip("Define expectations for query behavior when no agentUserId and no authenticated user, then remove this skip.")
    def test_query_without_fallback_no_agent_or_user(self):
        """Explicitly document behavior when there is no AGENT_USER_ID, no agentUserId, and no authenticated user on QUERY."""
        # TODO: Construct a QUERY request with no agentUserId and no authenticated user context
        # and assert the resulting behavior (e.g., error response, empty states, etc.)
        self.fail("Implement QUERY no-fallback/no-user behavior assertion")

    @unittest.skip("Define expectations for execute behavior when no agentUserId and no authenticated user, then remove this skip.")
    def test_execute_without_fallback_no_agent_or_user(self):
        """Explicitly document behavior when there is no AGENT_USER_ID, no agentUserId, and no authenticated user on EXECUTE."""
        # TODO: Construct an EXECUTE request with no agentUserId and no authenticated user context
        # and assert the resulting behavior (e.g., error response, no side effects, etc.)
        self.fail("Implement EXECUTE no-fallback/no-user behavior assertion")

    @unittest.skip("Wire this test to the helper that resolves user scope and assert its behavior with no fallback.")
    def test_user_scope_resolution_without_fallback(self):
        """Lock in behavior of whatever helper resolves smarthome user scope when no AGENT_USER_ID, no agentUserId, and no authenticated user."""
        # TODO: Call the helper used by SYNC/QUERY/EXECUTE to resolve user scope and
        # assert the expected behavior (e.g., returns None/raises, etc.) in the no-fallback case.
        self.fail("Implement user-scope helper no-fallback/no-user behavior assertion")

To fully implement the requested tests and remove the @unittest.skip decorators and self.fail placeholders, you will need to:

  1. Mirror the patterns used in your existing multi-tenant tests (likely in this same file) for constructing SYNC/QUERY/EXECUTE requests:

    • Reuse the same helpers or factories that build request payloads and contexts.
    • Ensure no agentUserId is included in the request payload.
    • Ensure there is no authenticated user attached (e.g., no session, no JWT, no user object in the context).
  2. For test_sync_without_fallback_no_agent_or_user:

    • Call into the same entry point your existing SYNC tests use (e.g., a Flask test client hitting the SYNC endpoint or direct invocation of onSync/actions.onSync with the appropriate arguments).
    • Assert the exact expected behavior in the no-fallback/no-user case (for example: HTTP 401/403, specific error payload, or an empty device list), and replace the self.fail with those assertions.
  3. For test_query_without_fallback_no_agent_or_user and test_execute_without_fallback_no_agent_or_user:

    • Use the same entry points used by your current QUERY/EXECUTE tests (e.g., actions.onQuery, actions.onExecute, or HTTP endpoints).
    • Assert the exact expected behavior (e.g., error code, empty response, or specific error structure) when no user scope can be resolved.
  4. For test_user_scope_resolution_without_fallback:

    • Import and call the helper that replaced _resolve_smarthome_user_scope (or _resolve_smarthome_user_scope if it still exists) that is responsible for resolving the user scope used by SYNC/QUERY/EXECUTE.
    • Construct its inputs to represent "no agentUserId and no authenticated user" and assert its behavior (e.g., returns None, raises a specific exception, or produces a special sentinel).
    • Update the test body to call this helper and assert on its return/exception instead of self.fail.

Once you wire these tests to the actual request/handler/helper APIs and define the assertions that match your intended "no fallback" default behavior, you can remove the @unittest.skip decorators so these tests actively lock in that behavior and prevent regressions if a fallback is reintroduced.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes the legacy AGENT_USER_ID configuration and the username field from the User model, transitioning to a session-based or explicit user identification system. It updates MQTT subscriptions to remove legacy topics and modifies the /me endpoint and authorization templates to use email instead of username. A review comment suggests ensuring consistency in fallback values for agentUserId in action_devices.py to avoid using the string "None" when identifiers are missing.

Comment thread action_devices.py Outdated
resolved_user = _normalize_user_scope(user_id)
fallback = resolved_user or current_app.config.get('AGENT_USER_ID', 'test-user')
agent_user = str(agent_user_id if agent_user_id is not None else fallback)
agent_user = str(agent_user_id if agent_user_id is not None else resolved_user)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When both agent_user_id and resolved_user are None, agent_user will be assigned the string value "None" due to the str() conversion. This is inconsistent with the error handling in this file (which uses "unknown" on line 191) and the report_state implementation (which uses "unknown" on line 337). Using "None" as an identifier may also cause issues with the Smart Home graph. Consider using a consistent fallback value.

Suggested change
agent_user = str(agent_user_id if agent_user_id is not None else resolved_user)
agent_user = str(agent_user_id if agent_user_id is not None else (resolved_user or "unknown"))

@DaTiC0

DaTiC0 commented May 5, 2026

Copy link
Copy Markdown
Owner Author

I have addressed all review comments:

  • Centralized Identifiers: Added UNKNOWN_USER_ID = "unknown" to ensure consistency.
  • Fixed onSync bug: Resolved the issue where agentUserId could become the literal string 'None'.
  • Strengthened Tests: Updated /api/me tests to assert the full response shape and added new test cases for 'no-fallback' scenarios in test_multi_tenant.py.
  • Linting: Removed unused current_app import in action_devices.py.
  • Verification: All 45 tests are passing.

@DaTiC0 DaTiC0 added the python Pull requests that update Python code label May 5, 2026
@DaTiC0
DaTiC0 merged commit 5c58242 into main May 5, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant