Skip to content

Conversation

@karlbeecken
Copy link
Contributor

This PR is to add a basic integration (containing only the sensor device class) for Teltonika routers and modems.

Breaking change

Proposed change

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

add basic integration with sensor device class
This includes only the licensing under Apache 2.0
@karlbeecken karlbeecken force-pushed the add-teltonika-integration branch from 2988a2a to 31e29e5 Compare November 29, 2025 17:11
mentions license properly in the pyproject.toml
@karlbeecken karlbeecken marked this pull request as ready for review November 29, 2025 18:03
Copilot AI review requested due to automatic review settings November 29, 2025 18:03
Copilot finished reviewing on behalf of karlbeecken November 29, 2025 18:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new integration for Teltonika routers and modems with sensor support for monitoring modem status (signal strength, temperature, operator, connection type).

Key Changes

  • Implements config flow with user setup, reauth, and DHCP discovery
  • Creates coordinator-based data updates with 30-second polling interval
  • Adds 7 sensor types: RSSI, RSRP, RSRQ, SINR, temperature, operator, and connection type
  • Includes comprehensive test coverage and diagnostics support

Reviewed changes

Copilot reviewed 24 out of 27 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
homeassistant/components/teltonika/__init__.py Entry point with async setup, device registration, and protocol fallback handling
homeassistant/components/teltonika/config_flow.py Config flow with user, reauth, and DHCP discovery flows
homeassistant/components/teltonika/coordinator.py Data update coordinator for fetching modem status
homeassistant/components/teltonika/sensor.py Sensor platform with 7 sensor types for modem monitoring
homeassistant/components/teltonika/diagnostics.py Diagnostics support with data serialization
homeassistant/components/teltonika/util.py Helper functions for URL construction
homeassistant/components/teltonika/const.py Domain and constant definitions
homeassistant/components/teltonika/strings.json User-facing translations for config flow and entities
homeassistant/components/teltonika/manifest.json Integration metadata with DHCP discovery configuration
homeassistant/components/teltonika/quality_scale.yaml Quality scale rule tracking
tests/components/teltonika/ Comprehensive test suite with config flow, init, sensor, diagnostics, and utility tests
requirements_all.txt, requirements_test_all.txt Added teltasync==0.1.3 dependency
CODEOWNERS Added @karlbeecken as code owner
Generated files Updated integration registry, DHCP matchers, and config flows list

Comment on lines +67 to +102
try:
await candidate_client.get_device_info()
system_info_response = await candidate_client.get_system_info()
except TeltonikaConnectionError as err:
last_error = err
await candidate_client.close()
except TeltonikaAuthenticationError as err:
await candidate_client.close()
raise ConfigEntryAuthFailed(f"Authentication failed: {err}") from err
except ContentTypeError as err:
# Device returned HTML instead of JSON - likely auth failure
await candidate_client.close()
if err.status == 403:
raise ConfigEntryAuthFailed(
f"Access denied - check credentials: {err}"
) from err
last_error = err
except ClientResponseError as err:
await candidate_client.close()
if err.status in (401, 403):
raise ConfigEntryAuthFailed(
f"Authentication failed (HTTP {err.status}): {err.message}"
) from err
last_error = err
except Exception as err: # pylint: disable=broad-except
await candidate_client.close()
# Check if error message indicates authentication issues
error_str = str(err).lower()
if any(
keyword in error_str
for keyword in ("auth", "unauthorized", "forbidden", "credentials")
):
raise ConfigEntryAuthFailed(f"Authentication failed: {err}") from err
last_error = err
else:
return candidate_client, candidate, system_info_response
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

[nitpick] The _try_connect function processes data outside the try/except blocks but doesn't follow the recommended pattern. After catching exceptions, the code should not continue to process the response. Line 102 returns system_info_response from inside the else block, which is correct. However, the try block should only contain the code that can throw exceptions (lines 68-69) for clarity and best practices.

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +59

# Return only modems which are online
modem_data = {}
if modems_response.data:
for modem in modems_response.data:
if Modems.is_online(modem):
modem_data[modem.id] = modem

except TeltonikaConnectionError as err:
raise UpdateFailed(f"Error communicating with device: {err}") from err
except Exception as err:
raise UpdateFailed(f"Unexpected error: {err}") from err
else:
return modem_data
Copy link

Copilot AI Nov 29, 2025

Choose a reason for hiding this comment

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

The try/except/else pattern here violates the recommended error handling approach. Lines 48-52 should be outside the try block because they process data and don't throw exceptions. Only lines 44-45 should be in the try block. The else clause should be removed and the return statement should be outside the try/except block.

Better pattern:

try:
    modems = Modems(self.client.auth)
    modems_response = await modems.get_status()
except TeltonikaConnectionError as err:
    raise UpdateFailed(f"Error communicating with device: {err}") from err
except Exception as err:
    raise UpdateFailed(f"Unexpected error: {err}") from err

# Process data outside try block
modem_data = {}
if modems_response.data:
    for modem in modems_response.data:
        if Modems.is_online(modem):
            modem_data[modem.id] = modem

return modem_data
Suggested change
# Return only modems which are online
modem_data = {}
if modems_response.data:
for modem in modems_response.data:
if Modems.is_online(modem):
modem_data[modem.id] = modem
except TeltonikaConnectionError as err:
raise UpdateFailed(f"Error communicating with device: {err}") from err
except Exception as err:
raise UpdateFailed(f"Unexpected error: {err}") from err
else:
return modem_data
except TeltonikaConnectionError as err:
raise UpdateFailed(f"Error communicating with device: {err}") from err
except Exception as err:
raise UpdateFailed(f"Unexpected error: {err}") from err
# Return only modems which are online
modem_data = {}
if modems_response.data:
for modem in modems_response.data:
if Modems.is_online(modem):
modem_data[modem.id] = modem
return modem_data

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 24 out of 27 changed files in this pull request and generated 5 comments.

karlbeecken and others added 4 commits November 29, 2025 19:55
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 24 out of 27 changed files in this pull request and generated no new comments.

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