diff --git a/API.md b/API.md index ea2a6f9..bae5b63 100644 --- a/API.md +++ b/API.md @@ -4,86 +4,126 @@ `from notecard import card` -| Notecard API | Python Library API | -| -----------------------| -------------------| -| `card.attn` | card.attn | -| `card.aux` | NOT IMPLEMENTED | -| `card.contact` | NOT IMPLEMENTED | -| `card.location.mode` | NOT IMPLEMENTED | -| `card.location.track` | NOT IMPLEMENTED | -| `card.motion.mode` | NOT IMPLEMENTED | -| `card.motion.sync` | NOT IMPLEMENTED | -| `card.motion.track` | NOT IMPLEMENTED | -| `card.restart` | NOT IMPLEMENTED | -| `card.restore` | NOT IMPLEMENTED | -| `card.status` | card.status | -| `card.temp` | card.temp | -| `card.time` | card.time | -| `card.usage.get` | NOT IMPLEMENTED | -| `card.usage.test` | NOT IMPLEMENTED | -| `card.version` | card.version | -| `card.voltage` | card.voltage | -| `card.wireless` | card.wireless | +| Notecard API | Python Library API | +| ----------------------- | ------------------- | +| `card.attn` | card.attn | +| `card.aux` | card.aux | +| `card.aux.serial` | card.auxSerial | +| `card.binary` | card.binary | +| `card.binary.get` | card.binaryGet | +| `card.binary.put` | card.binaryPut | +| `card.carrier` | card.carrier | +| `card.contact` | card.contact | +| `card.dfu` | card.dfu | +| `card.illumination` | card.illumination | +| `card.io` | card.io | +| `card.led` | card.led | +| `card.location` | card.location | +| `card.location.mode` | card.locationMode | +| `card.location.track` | card.locationTrack | +| `card.monitor` | NOT IMPLEMENTED | +| `card.motion` | NOT IMPLEMENTED | +| `card.motion.mode` | NOT IMPLEMENTED | +| `card.motion.sync` | NOT IMPLEMENTED | +| `card.motion.track` | NOT IMPLEMENTED | +| `card.power` | card.power | +| `card.restart` | NOT IMPLEMENTED | +| `card.restore` | NOT IMPLEMENTED | +| `card.sleep` | NOT IMPLEMENTED | +| `card.status` | card.status | +| `card.temp` | card.temp | +| `card.time` | card.time | +| `card.trace` | NOT IMPLEMENTED | +| `card.transport` | card.transport | +| `card.triangulate` | NOT IMPLEMENTED | +| `card.usage.get` | NOT IMPLEMENTED | +| `card.usage.test` | NOT IMPLEMENTED | +| `card.version` | card.version | +| `card.voltage` | card.voltage | +| `card.wifi` | NOT IMPLEMENTED | +| `card.wireless` | card.wireless | +| `card.wireless.penalty` | NOT IMPLEMENTED | -## Note Commands +## DFU Commands -`from notecard import note` +| Notecard API | Python Library API | +| ------------ | ------------------ | +| `dfu.get` | NOT IMPLEMENTED | +| `dfu.set` | NOT IMPLEMENTED | + +## Env Commands -| Notecard API | Python Library API | -| -----------------------| -------------------| -| `note.add` | note.add | -| `note.changes` | note.changes | -| `note.delete` | note.delete | -| `note.get` | note.get | -| `note.update` | note.update | -| `note.template` | note.template | +`from notecard import env` + +| Notecard API | Python Library API | +| -------------- | ------------------ | +| `env.default` | env.default | +| `env.get` | env.get | +| `env.modified` | env.modified | +| `env.set` | env.set | +| `env.template` | NOT IMPLEMENTED | + +## File Commands + +`from notecard import file` + +| Notecard API | Python Library API | +| ---------------------- | ------------------- | +| `file.changes` | file.changes | +| `file.delete` | file.delete | +| `file.stats` | file.stats | +| `file.changes.pending` | file.pendingChanges | ## Hub Commands `from notecard import hub` -| Notecard API | Python Library API | -| -----------------------| -------------------| -| `hub.get` | hub.get | -| `hub.log` | hub.log | -| `hub.set` | hub.set | -| `hub.status` | hub.status | -| `hub.sync` | hub.sync | -| `hub.sync.status` | hub.syncStatus | +| Notecard API | Python Library API | +| ----------------- | ------------------ | +| `hub.get` | hub.get | +| `hub.log` | hub.log | +| `hub.set` | hub.set | +| `hub.status` | hub.status | +| `hub.sync` | hub.sync | +| `hub.sync.status` | hub.syncStatus | -## DFU Commands +## Note Commands -| Notecard API | Python Library API | -| -----------------------| -------------------| -| `dfu.get` | NOT IMPLEMENTED | -| `dfu.set` | NOT IMPLEMENTED | +`from notecard import note` -## Env Commands +| Notecard API | Python Library API | +| --------------- | ------------------ | +| `note.add` | note.add | +| `note.changes` | note.changes | +| `note.delete` | note.delete | +| `note.get` | note.get | +| `note.update` | note.update | +| `note.template` | note.template | -`from notecard import env` +## NTN Commands -| Notecard API | Python Library API | -| -----------------------| -------------------| -| `env.default` | env.default | -| `env.get` | env.get | -| `env.modified` | env.modified | -| `set` | env.set | +`from notecard import ntn` -## File Commands +| Notecard API | Python Library API | +| ------------ | ------------------ | +| `ntn.gps` | NOT IMPLEMENTED | +| `ntn.reset` | NOT IMPLEMENTED | +| `ntn.status` | NOT IMPLEMENTED | -`from notecard import file` +## Var Commands -| Notecard API | Python Library API | -| -----------------------| -------------------| -| `file.changes` | file.changes | -| `file.delete` | file.delete | -| `file.stats` | file.stats | -| `file.changes.pending` | file.pendingChanges| +| Notecard API | Python Library API | +| ------------ | ------------------ | +| `var.delete` | NOT IMPLEMENTED | +| `var.get` | NOT IMPLEMENTED | +| `var.set` | NOT IMPLEMENTED | ## Web Commands -| Notecard API | Python Library API | -| -----------------------| -------------------| -| `web.get` | NOT IMPLEMENTED | -| `web.post` | NOT IMPLEMENTED | -| `web.put` | NOT IMPLEMENTED | +| Notecard API | Python Library API | +| ------------ | ------------------ | +| `web.get` | NOT IMPLEMENTED | +| `web.post` | NOT IMPLEMENTED | +| `web.put` | NOT IMPLEMENTED | +| `web.delete` | NOT IMPLEMENTED | +| `web` | NOT IMPLEMENTED | diff --git a/notecard/card.py b/notecard/card.py index 5b71f23..0f574a9 100644 --- a/notecard/card.py +++ b/notecard/card.py @@ -207,3 +207,448 @@ def power(card, minutes=None, reset=None): if reset: req["reset"] = reset return card.Transaction(req) + + +@validate_card_object +def location(card): + """Retrieve the last known location of the Notecard. + + Args: + card (Notecard): The current Notecard object. + + Returns: + dict: The result of the Notecard request containing location information including: + "status": The current status of the Notecard GPS/GNSS connection + "mode": The GPS/GNSS connection mode (continuous, periodic, or off) + "lat": The latitude in degrees of the last known location + "lon": The longitude in degrees of the last known location + "time": UNIX Epoch time of location capture + "max": If a geofence is enabled by card.location.mode, meters from the geofence center + "count": The number of consecutive recorded GPS/GNSS failures + "dop": The "Dilution of Precision" value from the latest GPS/GNSS reading + """ + req = {"req": "card.location"} + return card.Transaction(req) + + +@validate_card_object +def locationMode(card, mode=None, seconds=None, vseconds=None, lat=None, lon=None, max=None): + """Set location-related configuration settings. + + Args: + card (Notecard): The current Notecard object. + mode (string): The location mode to set. Must be one of: + - "" (empty string) to retrieve the current mode + - "off" to turn location mode off + - "periodic" to sample location at a specified interval + - "continuous" to enable the Notecard's GPS/GNSS module for continuous sampling + - "fixed" to report the location as a fixed location + seconds (int): When in periodic mode, location will be sampled at this interval, if the Notecard detects motion. + vseconds (string): In periodic mode, overrides seconds with a voltage-variable value. + lat (float): Used with fixed mode to specify the latitude coordinate. + lon (float): Used with fixed mode to specify the longitude coordinate. + max (int): Maximum number of seconds to wait for a GPS fix. + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.location.mode"} + if mode is not None: + req["mode"] = mode + if seconds: + req["seconds"] = seconds + if vseconds: + req["vseconds"] = vseconds + if lat is not None: + req["lat"] = lat + if lon is not None: + req["lon"] = lon + if max: + req["max"] = max + return card.Transaction(req) + + +@validate_card_object +def locationTrack(card, start=None, heartbeat=None, hours=None, sync=None, stop=None, file=None): + """Store location data in a Notefile at the periodic interval, or using a specified heartbeat. + + Args: + card (Notecard): The current Notecard object. + start (bool): Set to True to start Notefile tracking. + heartbeat (bool): When start is True, set to True to enable tracking even when motion is not detected. + hours (int): If heartbeat is True, add a heartbeat entry at this hourly interval. + Use a negative integer to specify a heartbeat in minutes instead of hours. + sync (bool): Set to True to perform an immediate sync to the Notehub each time a new Note is added. + stop (bool): Set to True to stop Notefile tracking. + file (string): The name of the Notefile to store location data in. Defaults to "track.qo". + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.location.track"} + if start is not None: + req["start"] = start + if heartbeat is not None: + req["heartbeat"] = heartbeat + if hours: + req["hours"] = hours + if sync is not None: + req["sync"] = sync + if stop is not None: + req["stop"] = stop + if file: + req["file"] = file + return card.Transaction(req) + + +@validate_card_object +def binary(card, delete=None): + """View the status of the binary storage area of the Notecard and optionally clear data. + + Args: + card (Notecard): The current Notecard object. + delete (bool): Set to True to clear the COBS area on the Notecard and reset all related arguments. + + Returns: + dict: The result of the Notecard request containing binary storage information including: + "cobs": The size of COBS-encoded data stored in the reserved area + "connected": Returns True if the Notecard is connected to the network + "err": If present, a string describing the error that occurred during transmission + "length": The length of the binary data + "max": Available storage space + "status": MD5 checksum of unencoded buffer + """ + req = {"req": "card.binary"} + if delete is not None: + req["delete"] = delete + return card.Transaction(req) + + +@validate_card_object +def binaryGet(card, cobs=None, offset=None, length=None): + """Retrieve binary data stored in the binary storage area of the Notecard. + + Args: + card (Notecard): The current Notecard object. + cobs (int): The size of the COBS-encoded data you are expecting to be returned (in bytes). + offset (int): Used along with length, the number of bytes to offset the binary payload from 0 when retrieving binary data. + length (int): Used along with offset, the number of bytes to retrieve from the binary storage area. + + Returns: + dict: The result of the Notecard request. The response returns the JSON-formatted response object, then the binary data. + "status": The MD5 checksum of the data returned, after it has been decoded + "err": If present, a string describing the error that occurred during transmission + """ + req = {"req": "card.binary.get"} + if cobs: + req["cobs"] = cobs + if offset is not None: + req["offset"] = offset + if length: + req["length"] = length + return card.Transaction(req) + + +@validate_card_object +def binaryPut(card, offset=None, cobs=None, status=None): + """Add binary data to the binary storage area of the Notecard. + + Args: + card (Notecard): The current Notecard object. + offset (int): The number of bytes to offset the binary payload from 0 when appending the binary data to the binary storage area. + cobs (int): The size of the COBS-encoded data (in bytes). + status (string): The MD5 checksum of the data, before it has been encoded. + + Returns: + dict: The result of the Notecard request. The Notecard expects to receive binary data immediately following the usage of this API command. + "err": If present, a string describing the error that occurred during transmission + """ + req = {"req": "card.binary.put"} + if offset is not None: + req["offset"] = offset + if cobs: + req["cobs"] = cobs + if status: + req["status"] = status + return card.Transaction(req) + + +@validate_card_object +def carrier(card, mode=None): + """Configure the AUX_CHARGING pin to notify the Notecard about charging support on a Notecarrier. + + Args: + card (Notecard): The current Notecard object. + mode (string): The AUX_CHARGING mode. Set to "charging" to tell the Notecard that AUX_CHARGING + is connected to a Notecarrier that supports charging. Set to "-" or "off" to turn off + the AUX_CHARGING detection. + + Returns: + dict: The result of the Notecard request containing: + "mode": The current AUX_CHARGING mode, or "off" if not set + "charging": Will display True when in AUX_CHARGING "charging" mode + """ + req = {"req": "card.carrier"} + if mode: + req["mode"] = mode + return card.Transaction(req) + + +@validate_card_object +def contact(card, name=None, org=None, role=None, email=None): + """Set or retrieve information about the Notecard maintainer. + + Args: + card (Notecard): The current Notecard object. + name (string): Set the name of the Notecard maintainer. + org (string): Set the organization name of the Notecard maintainer. + role (string): Set the role of the Notecard maintainer. + email (string): Set the email address of the Notecard maintainer. + + Returns: + dict: The result of the Notecard request containing: + "name": Name of the Notecard maintainer + "org": Organization name of the Notecard maintainer + "role": Role of the Notecard maintainer + "email": Email address of the Notecard maintainer + """ + req = {"req": "card.contact"} + if name: + req["name"] = name + if org: + req["org"] = org + if role: + req["role"] = role + if email: + req["email"] = email + return card.Transaction(req) + + +@validate_card_object +def aux(card, mode=None, usage=None, seconds=None, max=None, start=None, gps=None, + rate=None, sync=None, file=None, connected=None, limit=None, sensitivity=None, + ms=None, count=None, offset=None): + """Configure various uses of the general-purpose I/O (GPIO) pins AUX1-AUX4 for tracking and sensing tasks. + + Args: + card (Notecard): The current Notecard object. + mode (string): The AUX mode. Options include: "dfu", "gpio", "led", "monitor", "motion", + "neo", "neo-monitor", "off", "track", "track-monitor", "track-neo-monitor". + usage (array): An ordered list of pin modes for each AUX pin when in GPIO mode. + seconds (int): When in gpio mode, if an AUX pin is configured as a count type, + the count of rising edges can be broken into samples of this duration. + max (int): When in gpio mode, if an AUX pin is configured as a count type, + the maximum number of samples of duration seconds. + start (bool): When in gpio mode, if an AUX pin is configured as a count type, + set to True to reset counters and start incrementing. + gps (bool): Deprecated. If True, along with mode:track the Notecard supports + the use of an external GPS module. + rate (int): The AUX UART baud rate for debug communication over the AUXRX and AUXTX pins. + sync (bool): If True, for pins set as input by usage, the Notecard will autonomously + report any state changes as new notes in file. + file (string): The name of the Notefile used to report state changes when used + in conjunction with sync:True. + connected (bool): If True, defers the sync of the state change Notefile to the next + sync as configured by the hub.set request. + limit (bool): If True, along with mode:track and gps:True the Notecard will disable + concurrent modem use during GPS tracking. + sensitivity (int): When used with mode:neo-monitor or mode:track-neo-monitor, + this controls the brightness of NeoPixel lights. + ms (int): When in gpio mode, this argument configures a debouncing interval. + count (int): When used with mode:neo-monitor or mode:track-neo-monitor, + this controls the number of NeoPixels to use in a strip. + offset (int): When used with mode:neo-monitor or mode:track-neo-monitor, + this is the 1-based index in a strip of NeoPixels. + + Returns: + dict: The result of the Notecard request containing: + "mode": The current mode of the AUX interface + "text": Text received over the AUX interface + "binary": Binary data received over the AUX interface + "count": Number of bytes received + """ + req = {"req": "card.aux"} + if mode: + req["mode"] = mode + if usage: + req["usage"] = usage + if seconds: + req["seconds"] = seconds + if max: + req["max"] = max + if start is not None: + req["start"] = start + if gps is not None: + req["gps"] = gps + if rate: + req["rate"] = rate + if sync is not None: + req["sync"] = sync + if file: + req["file"] = file + if connected is not None: + req["connected"] = connected + if limit is not None: + req["limit"] = limit + if sensitivity: + req["sensitivity"] = sensitivity + if ms: + req["ms"] = ms + if count: + req["count"] = count + if offset: + req["offset"] = offset + return card.Transaction(req) + + +@validate_card_object +def auxSerial(card, mode=None, duration=None, rate=None, limit=None, max=None, ms=None, minutes=None): + """Configure various uses of the AUXTX and AUXRX pins on the Notecard's edge connector. + + Args: + card (Notecard): The current Notecard object. + mode (string): The AUX mode. Must be one of the following: + "req" - Request/response monitoring (default) + "gps" - Use external GPS/GNSS module + "notify" - Stream data or notifications + "notify,accel" - Stream accelerometer readings + "notify,signals" - Notify of Inbound Signals + "notify,env" - Notify of Environment Variable changes + "notify,dfu" - Notify of DFU events + duration (int): For mode "accel", specify sampling duration for accelerometer. + rate (int): Baud rate for transmission (default 115200, 9600 for GPS). + limit (bool): Disable concurrent modem use during GPS tracking. + max (int): Maximum data to send per session in bytes. + ms (int): Delay in milliseconds before sending buffer. + minutes (int): Interval for notifying host when using mode "dfu". + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.aux.serial"} + if mode: + req["mode"] = mode + if duration: + req["duration"] = duration + if rate: + req["rate"] = rate + if limit is not None: + req["limit"] = limit + if max: + req["max"] = max + if ms: + req["ms"] = ms + if minutes: + req["minutes"] = minutes + return card.Transaction(req) + + +@validate_card_object +def dfu(card, name=None, on=None, off=None, seconds=None, stop=None, start=None, mode=None): + """Configure a Notecard for Notecard Outboard Firmware Update. + + Args: + card (Notecard): The current Notecard object. + name (string): One of the supported classes of host MCU. Supported MCU classes are + 'esp32', 'stm32', 'stm32-bi', 'mcuboot', '-'. + on (bool): Set to True to enable Notecard Outboard Firmware Update. + off (bool): Set to True to disable Notecard Outboard Firmware Update from occurring. + seconds (int): When used with 'off':True, disable Notecard Outboard Firmware Update + operations for the specified number of seconds. + stop (bool): Set to True to disable the host RESET that is normally performed on the + host MCU when the Notecard starts up. + start (bool): Set to True to enable the host RESET. + mode (string): Optional mode for alternative DFU configuration. + + Returns: + dict: The result of the Notecard request containing: + "name": Current MCU class configured for DFU + """ + req = {"req": "card.dfu"} + if name: + req["name"] = name + if on is not None: + req["on"] = on + if off is not None: + req["off"] = off + if seconds: + req["seconds"] = seconds + if stop is not None: + req["stop"] = stop + if start is not None: + req["start"] = start + if mode: + req["mode"] = mode + return card.Transaction(req) + + +@validate_card_object +def illumination(card): + """Retrieve an illumination reading from an OPT3001 ambient light sensor connected to Notecard's I2C bus. + + Args: + card (Notecard): The current Notecard object. + + Returns: + dict: The result of the Notecard request containing: + "value": An illumination reading (in lux) from the attached OPT3001 sensor. + + Note: + If no OPT3001 sensor is detected, this request returns an "illumination sensor is not available" error. + """ + req = {"req": "card.illumination"} + return card.Transaction(req) + + +@validate_card_object +def io(card, i2c=None, mode=None): + """Override the Notecard's I2C address and change behaviors of the onboard LED and USB port. + + Args: + card (Notecard): The current Notecard object. + i2c (int): The alternate address to use for I2C communication. Pass -1 to reset to the default address. + mode (string): Mode to change LED or USB behavior. Options include: + "-usb" - Disable the Notecard's USB port. Re-enable with "usb" or "+usb" + "+busy" - LED will be on when Notecard is awake, off when asleep + "-busy" - Reset "+busy" to default (LED blinks only during flash operations) + "i2c-master-disable" - Disable Notecard acting as an I2C master + "i2c-master-enable" - Re-enable I2C master functionality + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.io"} + if i2c is not None: + req["i2c"] = i2c + if mode: + req["mode"] = mode + return card.Transaction(req) + + +@validate_card_object +def led(card, mode=None, on=None, off=None): + """Control connected LEDs or manage a single connected NeoPixel. + + Args: + card (Notecard): The current Notecard object. + mode (string): Used to specify the color of the LED or NeoPixel to control. + For LEDs: 'red', 'green', 'yellow' + For NeoPixels: 'red', 'green', 'blue', 'yellow', 'cyan', 'magenta', 'orange', 'white', 'gray' + on (bool): Set to True to turn the specified LED or NeoPixel on. + off (bool): Set to True to turn the specified LED or NeoPixel off. + + Returns: + dict: The result of the Notecard request. + + Note: + Requires the card.aux API to be configured in 'led' or 'neo' mode first. + Not supported by Notecard LoRa for regular LEDs. + """ + req = {"req": "card.led"} + if mode: + req["mode"] = mode + if on is not None: + req["on"] = on + if off is not None: + req["off"] = off + return card.Transaction(req) diff --git a/test/fluent_api/test_card.py b/test/fluent_api/test_card.py index 7794558..bc05621 100644 --- a/test/fluent_api/test_card.py +++ b/test/fluent_api/test_card.py @@ -69,6 +69,146 @@ 'minutes': 10, 'reset': True } + ), + ( + card.location, + 'card.location', + {} + ), + ( + card.locationMode, + 'card.location.mode', + { + 'mode': 'periodic', + 'seconds': 300, + 'vseconds': 'high', + 'lat': 42.5776, + 'lon': -70.87134, + 'max': 60 + } + ), + ( + card.locationTrack, + 'card.location.track', + { + 'start': True, + 'heartbeat': True, + 'hours': 2, + 'sync': True, + 'stop': False, + 'file': 'location.qo' + } + ), + ( + card.binary, + 'card.binary', + { + 'delete': True + } + ), + ( + card.binaryGet, + 'card.binary.get', + { + 'cobs': 1024, + 'offset': 0, + 'length': 512 + } + ), + ( + card.binaryPut, + 'card.binary.put', + { + 'offset': 0, + 'cobs': 1024, + 'status': 'd41d8cd98f00b204e9800998ecf8427e' + } + ), + ( + card.carrier, + 'card.carrier', + { + 'mode': 'charging' + } + ), + ( + card.contact, + 'card.contact', + { + 'name': 'Tom Turkey', + 'org': 'Blues', + 'role': 'Head of Security', + 'email': 'tom@blues.com' + } + ), + ( + card.aux, + 'card.aux', + { + 'mode': 'gpio', + 'usage': ['input', 'output', 'count'], + 'seconds': 60, + 'max': 10, + 'start': True, + 'gps': False, + 'rate': 115200, + 'sync': True, + 'file': 'aux.qo', + 'connected': False, + 'limit': True, + 'sensitivity': 50, + 'ms': 100, + 'count': 5, + 'offset': 1 + } + ), + ( + card.auxSerial, + 'card.aux.serial', + { + 'mode': 'notify,accel', + 'duration': 30, + 'rate': 9600, + 'limit': True, + 'max': 1024, + 'ms': 500, + 'minutes': 5 + } + ), + ( + card.dfu, + 'card.dfu', + { + 'name': 'esp32', + 'on': True, + 'off': False, + 'seconds': 300, + 'stop': True, + 'start': False, + 'mode': 'secure' + } + ), + ( + card.illumination, + 'card.illumination', + {} + ), + ( + card.io, + 'card.io', + { + 'i2c': 24, + 'mode': '+busy' + } + ), + ( + card.led, + 'card.led', + { + 'mode': 'red', + 'on': True, + 'off': False + } ) ] )