Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f4761cf
Potential fix for code scanning alert no. 44: Clear-text logging of s…
eman Oct 10, 2025
21f6b59
linter fixes
eman Oct 10, 2025
8884419
Potential fix for code scanning alert no. 46: Clear-text logging of s…
eman Oct 10, 2025
445733a
Potential fix for code scanning alert no. 47: Clear-text logging of s…
eman Oct 10, 2025
40d3143
Potential fix for code scanning alert no. 50: Clear-text logging of s…
eman Oct 10, 2025
efb8c1c
Potential fix for code scanning alert no. 48: Clear-text logging of s…
eman Oct 10, 2025
414f88b
Potential fix for code scanning alert no. 55: Clear-text logging of s…
eman Oct 10, 2025
8c4efba
Potential fix for code scanning alert no. 51: Clear-text logging of s…
eman Oct 10, 2025
1967c48
Potential fix for code scanning alert no. 53: Clear-text logging of s…
eman Oct 10, 2025
5ff56c0
Potential fix for code scanning alert no. 49: Clear-text logging of s…
eman Oct 10, 2025
a6adf39
Potential fix for code scanning alert no. 56: Clear-text logging of s…
eman Oct 10, 2025
b7bf057
Potential fix for code scanning alert no. 54: Clear-text logging of s…
eman Oct 10, 2025
5a1cc2e
Potential fix for code scanning alert no. 57: Clear-text logging of s…
eman Oct 10, 2025
c26c508
Potential fix for code scanning alert no. 52: Clear-text logging of s…
eman Oct 10, 2025
47abb81
Potential fix for code scanning alert no. 59: Clear-text logging of s…
eman Oct 10, 2025
a71f0ac
linting fixes
eman Oct 10, 2025
075d944
avoid displaying mac address
eman Oct 10, 2025
3d284d3
Potential fix for code scanning alert no. 64: Clear-text logging of s…
eman Oct 10, 2025
fa62a63
Potential fix for code scanning alert no. 65: Clear-text logging of s…
eman Oct 10, 2025
3f9ab33
Potential fix for code scanning alert no. 67: Clear-text logging of s…
eman Oct 10, 2025
3a64309
Potential fix for code scanning alert no. 66: Clear-text logging of s…
eman Oct 10, 2025
e645dcd
redact mac addresses in examples
eman Oct 10, 2025
e85eea3
Potential fix for code scanning alert no. 72: Clear-text logging of s…
eman Oct 10, 2025
14bb4ca
Potential fix for code scanning alert no. 68: Clear-text logging of s…
eman Oct 10, 2025
94ee37a
Potential fix for code scanning alert no. 73: Clear-text logging of s…
eman Oct 10, 2025
63a7e35
Potential fix for code scanning alert no. 69: Clear-text logging of s…
eman Oct 10, 2025
1c2b6a3
Potential fix for code scanning alert no. 71: Clear-text logging of s…
eman Oct 10, 2025
b76fcf6
Potential fix for code scanning alert no. 74: Clear-text logging of s…
eman Oct 10, 2025
e235a9a
Potential fix for code scanning alert no. 76: Clear-text logging of s…
eman Oct 10, 2025
fbe4641
Potential fix for code scanning alert no. 78: Clear-text logging of s…
eman Oct 10, 2025
7322be0
Potential fix for code scanning alert no. 77: Clear-text logging of s…
eman Oct 10, 2025
9128836
linter fixes
eman Oct 10, 2025
e1fe59c
fix mac address printing
eman Oct 10, 2025
d66d916
Potential fix for code scanning alert no. 80: Clear-text logging of s…
eman Oct 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/.ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
line-length = 120

[lint]
extend-ignore = ["E402"]
75 changes: 54 additions & 21 deletions examples/api_client_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

# Setup logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)

# If running from examples directory, add parent to path
Expand All @@ -24,6 +25,14 @@
from nwp500.api_client import APIError
from nwp500.auth import AuthenticationError, NavienAuthClient

import re


def mask_mac(mac: str) -> str:
"""Redact all MAC addresses in the input string."""
mac_regex = r"([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}|([0-9A-Fa-f]{12})"
return re.sub(mac_regex, "[REDACTED_MAC]", mac)


async def example_basic_usage():
"""Basic usage example."""
Expand Down Expand Up @@ -52,19 +61,39 @@ async def example_basic_usage():
print(f"✅ Found {len(devices)} device(s)\n")

# Display device information
try:
from examples.mask import mask_mac # type: ignore
except Exception:
# fallback helper if import fails when running examples directly

def mask_mac(mac: str) -> str: # pragma: no cover - small fallback
# Always return "[REDACTED_MAC]" regardless of input for safety
return "[REDACTED_MAC]"

try:
from examples.mask import mask_any, mask_location # type: ignore
except Exception:

def mask_any(_):
return "[REDACTED]"

def mask_location(_, __):
return "[REDACTED_LOCATION]"

for i, device in enumerate(devices, 1):
info = device.device_info
loc = device.location

print(f"Device {i}: {info.device_name}")
print(f" MAC Address: {info.mac_address}")
print(f" Type: {info.device_type}")
print(f" MAC Address: {mask_mac(info.mac_address)}")
print(f" Type: {mask_any(info.device_type)}")
print(f" Connection Status: {info.connected}")

loc_mask = mask_location(loc.city, loc.state)
if loc_mask:
print(f" Location: {loc_mask}")
if loc.address:
print(f" Location: {loc.address}")
if loc.city and loc.state:
print(f" {loc.city}, {loc.state}")
print(" Address: [REDACTED]")
print()

# Get detailed info for first device
Expand All @@ -80,10 +109,7 @@ async def example_basic_usage():
if detailed_info.device_info.install_type:
print(f" Install Type: {detailed_info.device_info.install_type}")
if detailed_info.location.latitude:
print(
f" Coordinates: {detailed_info.location.latitude}, "
f"{detailed_info.location.longitude}"
)
print(" Coordinates: (available, not shown for privacy)")
print()

# Get firmware information
Expand Down Expand Up @@ -113,11 +139,9 @@ async def example_basic_usage():
print(f" Error code: {e.code}")
return 1

except Exception as e:
print(f"\n❌ Unexpected error: {str(e)}")
import traceback

traceback.print_exc()
except Exception:
# Avoid printing raw exception details to stdout in examples
logging.exception("Unexpected error in api_client_example")
return 1


Expand All @@ -142,14 +166,23 @@ async def example_convenience_function():

print(f"✅ Found {len(devices)} device(s):\n")

try:
from examples.mask import mask_any, mask_location # type: ignore
except Exception:

def mask_any(_):
return "[REDACTED]"

def mask_location(_, __):
return "[REDACTED_LOCATION]"

for device in devices:
print(f" • {device.device_info.device_name}")
print(f" MAC: {device.device_info.mac_address}")
print(f" Type: {device.device_info.device_type}")
if device.location.city:
print(
f" Location: {device.location.city}, {device.location.state}"
)
print(f" MAC: {mask_mac(device.device_info.mac_address)}")
print(f" Type: {mask_any(device.device_info.device_type)}")
loc_mask = mask_location(device.location.city, device.location.state)
if loc_mask:
print(f" Location: {loc_mask}")
print()

return 0
Expand Down
13 changes: 8 additions & 5 deletions examples/authenticate.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@

# Setup logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)

# If running from examples directory, add parent to path
if __name__ == "__main__":
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))

from nwp500.auth import AuthenticationError, InvalidCredentialsError, NavienAuthClient
from nwp500.auth import (
AuthenticationError,
InvalidCredentialsError,
NavienAuthClient,
)


async def main():
Expand Down Expand Up @@ -72,9 +77,7 @@ async def main():
if tokens.access_key_id:
print("\nAWS Credentials available for IoT/MQTT:")
print(f" Access Key ID: {tokens.access_key_id[:15]}...")
print(
f" Session Token: {tokens.session_token[:30] if tokens.session_token else 'N/A'}..."
)
print(f" Session Token: {tokens.session_token[:30] if tokens.session_token else 'N/A'}...")

except InvalidCredentialsError as e:
print(f"\n❌ Invalid credentials: {e.message}")
Expand Down
15 changes: 5 additions & 10 deletions examples/combined_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

# Setup logging
logging.basicConfig(
level=logging.WARNING, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
level=logging.WARNING,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logging.getLogger("nwp500.mqtt_client").setLevel(logging.INFO)
logging.getLogger("nwp500.auth").setLevel(logging.INFO)
Expand All @@ -40,9 +41,7 @@ async def main():
password = os.getenv("NAVIEN_PASSWORD")

if not email or not password:
print(
"❌ Error: Please set NAVIEN_EMAIL and NAVIEN_PASSWORD environment variables"
)
print("❌ Error: Please set NAVIEN_EMAIL and NAVIEN_PASSWORD environment variables")
return 1

print("=" * 70)
Expand All @@ -55,9 +54,7 @@ async def main():
print(f"✅ Authenticated as: {auth_client.current_user.full_name}")
print()

api_client = NavienAPIClient(
auth_client=auth_client, session=auth_client._session
)
api_client = NavienAPIClient(auth_client=auth_client, session=auth_client._session)
devices = await api_client.list_devices()

if not devices:
Expand Down Expand Up @@ -95,9 +92,7 @@ def on_feature(feature: DeviceFeature):
print(f"\n📋 Feature Info #{counts['feature']}")
print(f" Serial: {feature.controllerSerialNumber}")
print(f" FW Version: {feature.controllerSwVersion}")
print(
f" Temp Range: {feature.dhwTemperatureMin}-{feature.dhwTemperatureMax}°F"
)
print(f" Temp Range: {feature.dhwTemperatureMin}-{feature.dhwTemperatureMax}°F")
print(f" Heat Pump: {'Yes' if feature.heatpumpUse == 2 else 'No'}")
print(f" Electric: {'Yes' if feature.electricUse == 2 else 'No'}")

Expand Down
15 changes: 5 additions & 10 deletions examples/command_queue_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

# Setup logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)

from nwp500.auth import NavienAuthClient
Expand Down Expand Up @@ -110,9 +111,7 @@ def on_message(topic, message):

# Step 6: Simulate disconnection and queue commands
print("\n6. Simulating disconnection...")
print(
" Note: In real scenarios, this happens automatically during network issues"
)
print(" Note: In real scenarios, this happens automatically during network issues")

# Manually disconnect
await mqtt_client.disconnect()
Expand Down Expand Up @@ -146,9 +145,7 @@ def on_message(topic, message):
print(" Waiting for queued commands to be sent...")
await asyncio.sleep(3)

print(
f" ✅ Queue processed! Remaining: {mqtt_client.queued_commands_count}"
)
print(f" ✅ Queue processed! Remaining: {mqtt_client.queued_commands_count}")

# Step 9: Test queue limits
print("\n9. Testing queue limits...")
Expand All @@ -159,9 +156,7 @@ def on_message(topic, message):
for _i in range(config.max_queued_commands + 5):
await mqtt_client.request_device_status(device)

print(
f" Queue size: {mqtt_client.queued_commands_count} (max: {config.max_queued_commands})"
)
print(f" Queue size: {mqtt_client.queued_commands_count} (max: {config.max_queued_commands})")
print(" ✅ Queue properly limited (oldest commands dropped)")

# Clear queue
Expand Down
Loading