Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 35 additions & 3 deletions notecard/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
# This module contains helper methods for calling card.* Notecard API commands.
# This module is optional and not required for use with the Notecard.

import notecard
from notecard.validators import validate_card_object


Expand Down Expand Up @@ -106,7 +105,8 @@ def version(card):


@validate_card_object
def voltage(card, hours=None, offset=None, vmax=None, vmin=None):
def voltage(card, hours=None, offset=None, vmax=None, vmin=None,
usb=None, alert=None):
"""Retrieve current and historical voltage info from the Notecard.

Args:
Expand All @@ -115,9 +115,17 @@ def voltage(card, hours=None, offset=None, vmax=None, vmin=None):
offset (int): Number of hours to offset.
vmax (decimal): max voltage level to report.
vmin (decimal): min voltage level to report.
usb (bool): Enable USB power state monitoring.
alert (bool): Enable alerts for USB power state changes. Only works
when usb=True.

Returns:
string: The result of the Notecard request.
dict: The result of the Notecard request containing voltage and power
state information.

Note:
For Mojo-based power consumption monitoring with temperature and
milliamp-hour tracking, see card.power().
"""
req = {"req": "card.voltage"}
if hours:
Expand All @@ -128,6 +136,30 @@ def voltage(card, hours=None, offset=None, vmax=None, vmin=None):
req["vmax"] = vmax
if vmin:
req["vmin"] = vmin
if usb is not None:
req["usb"] = usb
if alert is not None:
req["alert"] = alert
return card.Transaction(req)


@validate_card_object
def power(card, minutes=None, reset=None):
"""Configure and query the Mojo-based power consumption monitoring.

Args:
card (Notecard): The current Notecard object.
minutes (int, optional): How often to log power consumption.
reset (bool, optional): Reset consumption counters if True.

Returns:
dict: Contains temperature, voltage, and milliamp_hours readings.
"""
req = {"req": "card.power"}
if minutes is not None:
req["minutes"] = minutes
if reset:
req["reset"] = True
return card.Transaction(req)


Expand Down
39 changes: 36 additions & 3 deletions notecard/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,29 @@ def default(card, name=None, text=None):


@validate_card_object
def get(card, name=None):
def get(card, name=None, names=None, time=None):
"""Perform an env.get request against a Notecard.

Args:
card (Notecard): The current Notecard object.
name (string): The name of an environment variable to get.
name (str, optional): The name of an environment variable to get.
names (list, optional): List of environment variable names to retrieve.
time (int, optional): UNIX epoch time to get variables modified after.

Returns:
string: The result of the Notecard request.
dict: The result of the Notecard request containing either:
- text: Value of the requested variable if name was specified
- body: Object with name/value pairs if names was specified or
if neither name nor names was specified
- time: UNIX epoch time of the last variable change
"""
req = {"req": "env.get"}
if name:
req["name"] = name
if names:
req["names"] = names
if time is not None:
req["time"] = time
return card.Transaction(req)


Expand Down Expand Up @@ -82,3 +92,26 @@ def set(card, name=None, text=None):
if text:
req["text"] = text
return card.Transaction(req)


@validate_card_object
def template(card, body=None):
"""Perform an env.template request against a Notecard.

Args:
card (Notecard): The current Notecard object.
body (dict, optional): Schema with variable names and type hints.
Supported type hints:
- Boolean: true
- String: numeric string for max length (pre v3.2.1)
- Integer: 11-14, 18 (signed), 21-24 (unsigned)
- Float: 12.1 (2-byte), 14.1 (4-byte), 18.1 (8-byte)

Returns:
dict: The result of the Notecard request, including 'bytes' field
indicating storage size.
"""
req = {"req": "env.template"}
if body is not None:
req["body"] = body
return card.Transaction(req)
55 changes: 45 additions & 10 deletions notecard/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
# This module contains helper methods for calling file.* Notecard API commands.
# This module is optional and not required for use with the Notecard.

import notecard
from notecard.validators import validate_card_object


Expand All @@ -28,9 +27,21 @@ def changes(card, tracker=None, files=None):
req = {"req": "file.changes"}
if tracker:
req["tracker"] = tracker
if files:
if files is not None: # Allow empty list
req["files"] = files
return card.Transaction(req)

response = card.Transaction(req)
if "err" in response:
return response

# Only validate types when fields are present
if "total" in response and not isinstance(response["total"], int):
return {"err": "Malformed response: total must be an integer"}
if "changes" in response and not isinstance(response["changes"], int):
return {"err": "Malformed response: changes must be an integer"}
if "info" in response and not isinstance(response["info"], dict):
return {"err": "Malformed response: info must be a dictionary"}
return response


@validate_card_object
Expand All @@ -51,18 +62,34 @@ def delete(card, files=None):


@validate_card_object
def stats(card):
def stats(card, file=None):
"""Obtain statistics about local notefiles.

Args:
card (Notecard): The current Notecard object.
file (str, optional): Returns stats for the specified Notefile only.

Returns:
string: The result of the Notecard request.
dict: The result of the Notecard request containing:
- total (int): Total number of Notes across all Notefiles
- changes (int): Number of Notes pending sync
- sync (bool): True if sync is recommended based on pending notes
"""
req = {"req": "file.stats"}

return card.Transaction(req)
if file:
req["file"] = file
response = card.Transaction(req)
if "err" in response:
return response

# Only validate types when fields are present
if "total" in response and not isinstance(response["total"], int):
return {"err": "Malformed response: total must be an integer"}
if "changes" in response and not isinstance(response["changes"], int):
return {"err": "Malformed response: changes must be an integer"}
if "sync" in response and not isinstance(response["sync"], bool):
return {"err": "Malformed response: sync must be a boolean"}
return response


@validate_card_object
Expand All @@ -73,8 +100,16 @@ def pendingChanges(card):
card (Notecard): The current Notecard object.

Returns:
string: The result of the Notecard request.
dict: The result of the Notecard request.
"""
req = {"req": "file.changes.pending"}

return card.Transaction(req)
response = card.Transaction(req)
if "err" in response:
return response

# Only validate types when fields are present
if "total" in response and not isinstance(response["total"], int):
return {"err": "Malformed response: total must be an integer"}
if "changes" in response and not isinstance(response["changes"], int):
return {"err": "Malformed response: changes must be an integer"}
return response
20 changes: 17 additions & 3 deletions notecard/note.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,16 @@ def add(card, file=None, body=None, payload=None, sync=None, port=None):


@validate_card_object
def changes(card, file=None, tracker=None, maximum=None,
start=None, stop=None, deleted=None, delete=None):
def changes(
card,
file=None,
tracker=None,
maximum=None,
start=None,
stop=None,
deleted=None,
delete=None,
):
"""Incrementally retrieve changes within a Notefile.

Args:
Expand Down Expand Up @@ -178,10 +186,16 @@ def template(card, file=None, body=None, length=None, port=None, compact=False):
req["file"] = file
if body:
req["body"] = body
if length:
if length is not None:
req["length"] = length
if port:
req["port"] = port

format = None
if compact:
format = "compact"

if format == "compact":
req["format"] = "compact"

return card.Transaction(req)
3 changes: 3 additions & 0 deletions notecard/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

def validate_card_object(func):
"""Ensure that the passed-in card is a Notecard."""

@functools.wraps(func)
def wrap_validator(*args, **kwargs):
"""Check the instance of the passed-in card."""
Expand All @@ -22,11 +23,13 @@ def wrap_validator(*args, **kwargs):
return func(*args, **kwargs)

return wrap_validator

else:
# MicroPython and CircuitPython do not support
# functools. Do not perform validation for these platforms
def validate_card_object(func):
"""Skip validation."""

def wrap_validator(*args, **kwargs):
"""Check the instance of the passed-in card."""
card = args[0]
Expand Down
8 changes: 8 additions & 0 deletions test/fluent_api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
import notecard # noqa: E402


@pytest.fixture
def card():
"""Create a mock Notecard instance for testing."""
card = notecard.Notecard()
card.Transaction = MagicMock()
return card


@pytest.fixture
def run_fluent_api_notecard_api_mapping_test():
def _run_test(fluent_api, notecard_api_name, req_params, rename_map=None):
Expand Down
22 changes: 22 additions & 0 deletions test/fluent_api/test_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@
'vmin': 1.2
}
),
(
card.voltage,
'card.voltage',
{
'usb': True
}
),
(
card.voltage,
'card.voltage',
{
'alert': True
}
),
(
card.voltage,
'card.voltage',
{
'usb': True,
'alert': True
}
),
(
card.wireless,
'card.wireless',
Expand Down
Loading