telemetry¶
Telemetry Consent Management Module
This module provides functionality for managing user consent for telemetry data collection in the SIMATIC AI SDK. It implements a hierarchical consent system that respects user privacy choices across multiple configuration methods. Check if telemetry is allowed: >>> from simaticai.telemetry import is_telemetry_allowed >>> if is_telemetry_allowed(): ... # Send telemetry data ... pass Set consent programmatically (persistent): >>> from simaticai.telemetry import allow_telemetry, deny_telemetry >>> allow_telemetry() # Save consent to config file >>> deny_telemetry() # Save denial to config file Set consent for current session only: >>> from simaticai.telemetry import allow_telemetry_once, deny_telemetry_once >>> allow_telemetry_once() # Temporary, not saved >>> deny_telemetry_once() # Temporary, not saved Via environment variable: $ export SIMATICAI_TELEMETRY=true $ python my_script.py Via command line: $ simaticai telemetry --allow $ simaticai telemetry --deny $ simaticai telemetry --status The consent configuration is stored in a platform-specific user configuration directory (e.g., ~/.config/simaticai/consent.yml on Linux) and includes: - User's consent decision (allow_telemetry: true/false) - Timestamp of the decision - SDK version at time of consent - Method used to provide consent - Full consent information text If no consent decision has been made, calling is_telemetry_allowed() will raise a RuntimeError prompting the user to provide consent via the CLI command: $ simaticai telemetryKey Features
Usage
Configuration File
Exception Handling
get_consent_source() ¶
Get the source of the telemetry consent decision.
Returns:
| Name | Type | Description |
|---|---|---|
str | str | The source of consent (e.g., 'python method', 'environment variable', 'command line', 'none'). |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def get_consent_source() -> str:
"""Get the source of the telemetry consent decision.
Returns:
str: The source of consent (e.g., 'python method', 'environment variable', 'command line', 'none').
"""
return _CONSENT_SOURCE
get_user_config_dir_path() ¶
Get the user configuration directory path for the application.
Creates the directory if it doesn't exist.
Returns:
| Name | Type | Description |
|---|---|---|
Path | Path | Path object pointing to the user's configuration directory. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def get_user_config_dir_path() -> Path:
"""Get the user configuration directory path for the application.
Creates the directory if it doesn't exist.
Returns:
Path: Path object pointing to the user's configuration directory.
"""
return platformdirs.user_config_path(appname=_APP_NAME, appauthor=_APP_AUTHOR, roaming=True, ensure_exists=True)
is_telemetry_allowed_by_env_var() ¶
Check if telemetry is allowed via environment variable.
Checks the SIMATICAI_TELEMETRY environment variable for consent.
Returns:
| Name | Type | Description |
|---|---|---|
ConsentStatus | ConsentStatus | ALLOWED if env var is set to an affirmative value, DENIED if set to a negative value, UNDECIDED if not set. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def is_telemetry_allowed_by_env_var() -> ConsentStatus:
"""Check if telemetry is allowed via environment variable.
Checks the SIMATICAI_TELEMETRY environment variable for consent.
Returns:
ConsentStatus: ALLOWED if env var is set to an affirmative value,
DENIED if set to a negative value, UNDECIDED if not set.
"""
if _CONSENT_ENV_VAR not in os.environ:
return ConsentStatus.UNDECIDED
choice = os.getenv(_CONSENT_ENV_VAR, "").lower() in _CONSENT_AFFIRMATIVE
_logger.warning(f"Telemetry is {'allowed' if choice else 'denied'} by environment variable.")
if choice:
return ConsentStatus.ALLOWED
return ConsentStatus.DENIED
read_telemetry_consent_file() ¶
Read the telemetry consent configuration file.
Returns:
| Type | Description |
|---|---|
tuple[dict, Path] | tuple[dict, Path]: A tuple containing the consent data dictionary and the path to the consent file. |
Raises:
| Type | Description |
|---|---|
AssertionError | If the consent file exists but the user's choice is not readable. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def read_telemetry_consent_file() -> tuple[dict, Path]:
"""Read the telemetry consent configuration file.
Returns:
tuple[dict, Path]: A tuple containing the consent data dictionary and the path to the consent file.
Raises:
AssertionError: If the consent file exists but the user's choice is not readable.
"""
consent_file = get_user_config_dir_path() / _CONSENT_FILE_NAME
data = yaml_helper.read_yaml(consent_file)
if CONSENT_KEY not in data:
raise AssertionError("Consent file exists, but the user's choice is not readable.")
return data, consent_file
is_telemetry_allowed_by_config_file() ¶
Check if telemetry is allowed via configuration file.
Reads the consent file and returns the user's telemetry choice.
Returns:
| Name | Type | Description |
|---|---|---|
bool | bool | True if telemetry is allowed, False otherwise. |
Raises:
| Type | Description |
|---|---|
RuntimeError | If the consent file cannot be read or doesn't exist, prompting the user to make a consent decision. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def is_telemetry_allowed_by_config_file() -> bool:
"""Check if telemetry is allowed via configuration file.
Reads the consent file and returns the user's telemetry choice.
Returns:
bool: True if telemetry is allowed, False otherwise.
Raises:
RuntimeError: If the consent file cannot be read or doesn't exist,
prompting the user to make a consent decision.
"""
try:
data, _ = read_telemetry_consent_file()
choice = data.get(CONSENT_KEY, False)
if not isinstance(choice, bool):
choice = str(choice).lower() in _CONSENT_AFFIRMATIVE
_logger.warning(f"Telemetry is {'allowed' if choice else 'denied'} by configuration file.")
return choice
except BaseException as error:
global _CONSENT_SOURCE
_CONSENT_SOURCE = "none"
raise RuntimeError(f"\n\n{CONSENT_EXCEPTION_TEXT}\n") from error
is_telemetry_allowed() ¶
Check if telemetry is allowed based on all possible sources.
Checks consent in order of precedence: 1. Override set via allow_telemetry_once/deny_telemetry_once 2. Environment variable (SIMATICAI_TELEMETRY) 3. Configuration file
Returns:
| Name | Type | Description |
|---|---|---|
bool | bool | True if telemetry is allowed, False otherwise. |
Raises:
| Type | Description |
|---|---|
RuntimeError | If no consent decision has been made and no configuration exists. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def is_telemetry_allowed() -> bool:
"""Check if telemetry is allowed based on all possible sources.
Checks consent in order of precedence:
1. Override set via allow_telemetry_once/deny_telemetry_once
2. Environment variable (SIMATICAI_TELEMETRY)
3. Configuration file
Returns:
bool: True if telemetry is allowed, False otherwise.
Raises:
RuntimeError: If no consent decision has been made and no configuration exists.
"""
global _CONSENT_SOURCE
_CONSENT_SOURCE = "python method"
if ConsentStatus.UNDECIDED != _CONSENT_OVERRIDE:
return ConsentStatus.ALLOWED == _CONSENT_OVERRIDE
_CONSENT_SOURCE = "environment variable"
choice_env = is_telemetry_allowed_by_env_var()
if ConsentStatus.UNDECIDED != choice_env:
return ConsentStatus.ALLOWED == choice_env
_CONSENT_SOURCE = "command line"
return is_telemetry_allowed_by_config_file()
set_telemetry_consent(allow) ¶
Set the telemetry consent decision in the configuration file.
Creates or updates the consent file with the user's choice, timestamp, SDK version, and consent method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow | bool | True to allow telemetry, False to deny it. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Path | Path | Path to the consent file that was created or updated. |
Raises:
| Type | Description |
|---|---|
RuntimeError | If the consent file cannot be written. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def set_telemetry_consent(allow: bool) -> Path:
"""Set the telemetry consent decision in the configuration file.
Creates or updates the consent file with the user's choice, timestamp,
SDK version, and consent method.
Args:
allow (bool): True to allow telemetry, False to deny it.
Returns:
Path: Path to the consent file that was created or updated.
Raises:
RuntimeError: If the consent file cannot be written.
"""
consent_file = get_user_config_dir_path() / _CONSENT_FILE_NAME
try:
with open(consent_file, "w") as f:
# writing YAML manually to keep the file format as intended
f.write(f"{CONSENT_KEY}: {'true' if allow else 'false'}\n")
f.write(f"{CONSENT_KEY_TS}: '{datetime.now().isoformat()}'\n")
f.write(f"{CONSENT_KEY_SDK}: '{version('simaticai')}'\n")
f.write(f"{CONSENT_KEY_METHOD}: '{_CONSENT_SOURCE}'\n")
f.write("information: |\n")
for line in CONSENT_INFO.splitlines():
f.write(f" {line}\n")
except BaseException as error:
raise RuntimeError(f"Failed to save consent in '{consent_file}'.") from error
return consent_file
allow_telemetry() ¶
Allow telemetry and save the decision to the configuration file.
Sets the consent source to 'python method' and saves the consent decision.
Returns:
| Name | Type | Description |
|---|---|---|
Path | Path | Path to the consent file that was updated. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def allow_telemetry() -> Path:
"""Allow telemetry and save the decision to the configuration file.
Sets the consent source to 'python method' and saves the consent decision.
Returns:
Path: Path to the consent file that was updated.
"""
global _CONSENT_SOURCE
_CONSENT_SOURCE = "python method"
return set_telemetry_consent(True)
deny_telemetry() ¶
Deny telemetry and save the decision to the configuration file.
Sets the consent source to 'python method' and saves the consent decision.
Returns:
| Name | Type | Description |
|---|---|---|
Path | Path | Path to the consent file that was updated. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def deny_telemetry() -> Path:
"""Deny telemetry and save the decision to the configuration file.
Sets the consent source to 'python method' and saves the consent decision.
Returns:
Path: Path to the consent file that was updated.
"""
global _CONSENT_SOURCE
_CONSENT_SOURCE = "python method"
return set_telemetry_consent(False)
allow_telemetry_cli() ¶
Allow telemetry via command line interface and save the decision.
Sets the consent source to 'command line' and saves the consent decision.
Returns:
| Name | Type | Description |
|---|---|---|
Path | Path | Path to the consent file that was updated. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def allow_telemetry_cli() -> Path:
"""Allow telemetry via command line interface and save the decision.
Sets the consent source to 'command line' and saves the consent decision.
Returns:
Path: Path to the consent file that was updated.
"""
global _CONSENT_SOURCE
_CONSENT_SOURCE = "command line"
return set_telemetry_consent(True)
deny_telemetry_cli() ¶
Deny telemetry via command line interface and save the decision.
Sets the consent source to 'command line' and saves the consent decision.
Returns:
| Name | Type | Description |
|---|---|---|
Path | Path | Path to the consent file that was updated. |
Source code in docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.py
def deny_telemetry_cli() -> Path:
"""Deny telemetry via command line interface and save the decision.
Sets the consent source to 'command line' and saves the consent decision.
Returns:
Path: Path to the consent file that was updated.
"""
global _CONSENT_SOURCE
_CONSENT_SOURCE = "command line"
return set_telemetry_consent(False)
allow_telemetry_once() ¶
Allow telemetry for the current session only without saving to file.
Sets a temporary override that allows telemetry for this session. The decision is not persisted to the configuration file.Source code in
docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.pydef allow_telemetry_once():
"""Allow telemetry for the current session only without saving to file.
Sets a temporary override that allows telemetry for this session.
The decision is not persisted to the configuration file.
"""
global _CONSENT_OVERRIDE, _CONSENT_SOURCE
_CONSENT_SOURCE = "python method"
_CONSENT_OVERRIDE = ConsentStatus.ALLOWED
_logger.warning("Telemetry is enabled for this session.")
deny_telemetry_once() ¶
Deny telemetry for the current session only without saving to file.
Sets a temporary override that denies telemetry for this session. The decision is not persisted to the configuration file.Source code in
docs/industrial-ai-suite/sdk/simaticai/telemetry/consent.pydef deny_telemetry_once():
"""Deny telemetry for the current session only without saving to file.
Sets a temporary override that denies telemetry for this session.
The decision is not persisted to the configuration file.
"""
global _CONSENT_OVERRIDE, _CONSENT_SOURCE
_CONSENT_SOURCE = "python method"
_CONSENT_OVERRIDE = ConsentStatus.DENIED
_logger.warning("Telemetry is disabled for this session.")