diff --git a/API.md b/API.md index bae5b63..1ab07b1 100644 --- a/API.md +++ b/API.md @@ -21,28 +21,28 @@ | `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.monitor` | card.monitor | +| `card.motion` | card.motion | +| `card.motion.mode` | card.motionMode | +| `card.motion.sync` | card.motionSync | +| `card.motion.track` | card.motionTrack | | `card.power` | card.power | -| `card.restart` | NOT IMPLEMENTED | -| `card.restore` | NOT IMPLEMENTED | -| `card.sleep` | NOT IMPLEMENTED | +| `card.restart` | card.restart | +| `card.restore` | card.restore | +| `card.sleep` | card.sleep | | `card.status` | card.status | | `card.temp` | card.temp | | `card.time` | card.time | -| `card.trace` | NOT IMPLEMENTED | +| `card.trace` | card.trace | | `card.transport` | card.transport | -| `card.triangulate` | NOT IMPLEMENTED | -| `card.usage.get` | NOT IMPLEMENTED | -| `card.usage.test` | NOT IMPLEMENTED | +| `card.triangulate` | card.triangulate | +| `card.usage.get` | card.usageGet | +| `card.usage.test` | card.usageTest | | `card.version` | card.version | | `card.voltage` | card.voltage | -| `card.wifi` | NOT IMPLEMENTED | +| `card.wifi` | card.wifi | | `card.wireless` | card.wireless | -| `card.wireless.penalty` | NOT IMPLEMENTED | +| `card.wireless.penalty` | card.wirelessPenalty| ## DFU Commands diff --git a/notecard/card.py b/notecard/card.py index 0f574a9..d6533dd 100644 --- a/notecard/card.py +++ b/notecard/card.py @@ -652,3 +652,455 @@ def led(card, mode=None, on=None, off=None): if off is not None: req["off"] = off return card.Transaction(req) + + +@validate_card_object +def monitor(card, mode=None, count=None, usb=None): + """Configure AUX pins when in monitor mode. + + Args: + card (Notecard): The current Notecard object. + mode (string): Set LED color. Options: 'green', 'red', 'yellow'. + count (int): Number of pulses to send to AUX pin LED. Set this to 0 to return to default LED behavior. + usb (bool): Set to true to configure LED behavior so that it is only active when the Notecard is connected to USB power. + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.monitor"} + if mode: + req["mode"] = mode + if count is not None: + req["count"] = count + if usb is not None: + req["usb"] = usb + return card.Transaction(req) + + +@validate_card_object +def motion(card, minutes=None): + """Retrieve information about the Notecard's accelerometer motion and orientation. + + Args: + card (Notecard): The current Notecard object. + minutes (int): Amount of time to sample for buckets of accelerometer-measured movement. + + Returns: + dict: The result of the Notecard request containing: + "count": Number of accelerometer motion events since the last card.motion request + "alert": Boolean indicating free-fall detection since the last request + "motion": UNIX Epoch time of the last accelerometer motion event + "status": Comma-separated list of orientation events (e.g., "face-up", "portrait-down") + "seconds": Duration of each bucket of sample accelerometer movements (when minutes is provided) + "movements": Base-36 characters representing motion counts in each bucket + "mode": Current motion status of the Notecard (e.g., "stopped" or "moving") + """ + req = {"req": "card.motion"} + if minutes is not None: + req["minutes"] = minutes + return card.Transaction(req) + + +@validate_card_object +def motionMode(card, start=None, stop=None, seconds=None, sensitivity=None, motion=None): + """Configure accelerometer motion monitoring parameters. + + Args: + card (Notecard): The current Notecard object. + start (bool): Set to True to enable the Notecard accelerometer and start motion tracking. + stop (bool): Set to True to disable the Notecard accelerometer and stop motion tracking. + seconds (int): Period for each bucket of movements to be accumulated when minutes is used with card.motion. + sensitivity (int): Sets accelerometer sample rate with different sensitivity levels (default -1). + motion (int): Threshold for motion events to trigger motion status change between "moving" and "stopped". + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.motion.mode"} + if start is not None: + req["start"] = start + if stop is not None: + req["stop"] = stop + if seconds is not None: + req["seconds"] = seconds + if sensitivity is not None: + req["sensitivity"] = sensitivity + if motion is not None: + req["motion"] = motion + return card.Transaction(req) + + +@validate_card_object +def motionSync(card, start=None, stop=None, minutes=None, count=None, threshold=None): + """Configure automatic sync triggered by Notecard movement. + + Args: + card (Notecard): The current Notecard object. + start (bool): Set to True to start motion-triggered syncing. + stop (bool): Set to True to stop motion-triggered syncing. + minutes (int): Maximum frequency at which sync will be triggered. + count (int): Number of most recent motion buckets to examine. + threshold (int): Number of buckets that must indicate motion to trigger a sync. + If set to 0, sync occurs only on orientation changes. + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.motion.sync"} + if start is not None: + req["start"] = start + if stop is not None: + req["stop"] = stop + if minutes is not None: + req["minutes"] = minutes + if count is not None: + req["count"] = count + if threshold is not None: + req["threshold"] = threshold + return card.Transaction(req) + + +@validate_card_object +def motionTrack(card, start=None, stop=None, minutes=None, count=None, threshold=None, file=None, now=None): + """Configure automatic capture of accelerometer motion in a Notefile. + + Args: + card (Notecard): The current Notecard object. + start (bool): Set to True to start motion capture. + stop (bool): Set to True to stop motion capture. + minutes (int): Maximum period to capture Notes in the Notefile. + count (int): Number of most recent motion buckets to examine. + threshold (int): Number of buckets that must indicate motion to capture. + file (string): Notefile to use for motion capture Notes (default '_motion.qo'). + now (bool): Set to True to trigger immediate _motion.qo event on orientation change. + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.motion.track"} + if start is not None: + req["start"] = start + if stop is not None: + req["stop"] = stop + if minutes is not None: + req["minutes"] = minutes + if count is not None: + req["count"] = count + if threshold is not None: + req["threshold"] = threshold + if file is not None: + req["file"] = file + if now is not None: + req["now"] = now + return card.Transaction(req) + + +@validate_card_object +def restart(card): + """Perform a firmware restart of the Notecard. + + Args: + card (Notecard): The current Notecard object. + + Returns: + dict: The result of the Notecard request. + + Warning: + Not recommended for production applications due to potential increased + cellular data and consumption credit usage. + """ + req = {"req": "card.restart"} + return card.Transaction(req) + + +@validate_card_object +def restore(card, delete=None, connected=None): + """Reset Notecard configuration settings and/or deprovision from Notehub. + + Args: + card (Notecard): The current Notecard object. + delete (bool): Set to True to reset most Notecard configuration settings. + Does not reset Wi-Fi credentials or alternate I2C address. + Notecard will be unable to sync with Notehub until ProductUID is set again. + On Notecard LoRa, this parameter is required, though LoRaWAN configuration is retained. + connected (bool): Set to True to reset the Notecard on Notehub. + Will delete and deprovision the Notecard from Notehub on next connection. + Removes any Notefile templates used by the device. + + Returns: + dict: The result of the Notecard request. + """ + req = {"req": "card.restore"} + if delete is not None: + req["delete"] = delete + if connected is not None: + req["connected"] = connected + return card.Transaction(req) + + +@validate_card_object +def sleep(card, on=None, off=None, seconds=None, mode=None): + """Configure sleep mode for Notecard WiFi v2. + + Args: + card (Notecard): The current Notecard object. + on (bool): Set to True to enable sleep mode after 30 seconds of idleness. + off (bool): Set to True to disable sleep mode. + seconds (int): Number of seconds before entering sleep mode (minimum 30). + mode (string): Accelerometer wake configuration. + Use "accel" to wake from deep sleep on accelerometer movement, + or "-accel" to reset to default setting. + + Returns: + dict: The result of the Notecard request containing: + "on": Boolean indicating if sleep mode is enabled + "off": Boolean indicating if sleep mode is disabled + "seconds": Configured sleep delay + "mode": Accelerometer wake configuration + + Note: + Only valid for Notecard WiFi v2. + """ + req = {"req": "card.sleep"} + if on is not None: + req["on"] = on + if off is not None: + req["off"] = off + if seconds is not None: + req["seconds"] = seconds + if mode: + req["mode"] = mode + return card.Transaction(req) + + +@validate_card_object +def trace(card, mode=None): + """Enable and disable trace mode on a Notecard for debugging. + + Args: + card (Notecard): The current Notecard object. + mode (string): Set to "on" to enable trace mode on a Notecard, or "off" to disable it. + + Returns: + dict: The result of the Notecard request. + + Note: + See: https://dev.blues.io/guides-and-tutorials/notecard-guides/using-notecard-trace-mode + """ + req = {"req": "card.trace"} + if mode: + req["mode"] = mode + return card.Transaction(req) + + +@validate_card_object +def triangulate(card, mode=None, on=None, usb=None, set=None, minutes=None, text=None, time=None): + """Enable or disable triangulation behavior for gathering cell tower and Wi-Fi access point information. + + Args: + card (Notecard): The current Notecard object. + mode (string): The triangulation approach to use. Keywords can be used separately or together + in a comma-delimited list: "cell", "wifi", or "-" to clear the mode. + on (bool): Set to True to triangulate even if the module has not moved. + Only takes effect when set is True. Default: False. + usb (bool): Set to True to perform triangulation only when connected to USB power. + Only takes effect when set is True. Default: False. + set (bool): Set to True to instruct the module to use the state of the on and usb arguments. + Default: False. + minutes (int): Minimum delay, in minutes, between triangulation attempts. + Use 0 for no time-based suppression. Default: 0. + text (string): When using Wi-Fi triangulation, a newline-terminated list of Wi-Fi access points. + Format should follow ESP32's AT+CWLAP command output. + time (int): UNIX Epoch time when the Wi-Fi access point scan was performed. + If not provided, Notecard time is used. + + Returns: + dict: The result of the Notecard request containing: + "motion": UNIX Epoch time of last detected Notecard movement + "time": UNIX Epoch time of last triangulation scan + "mode": Comma-separated list indicating active triangulation modes + "on": Boolean if triangulation scans will be performed even if device has not moved + "usb": Boolean if triangulation scans will be performed only when USB-powered + "length": Length of the text buffer provided in current or previous request + + Note: + See: https://dev.blues.io/notecard/notecard-walkthrough/time-and-location-requests/#using-cell-tower-and-wi-fi-triangulation + """ + req = {"req": "card.triangulate"} + if mode: + req["mode"] = mode + if on is not None: + req["on"] = on + if usb is not None: + req["usb"] = usb + if set is not None: + req["set"] = set + if minutes is not None: + req["minutes"] = minutes + if text: + req["text"] = text + if time is not None: + req["time"] = time + return card.Transaction(req) + + +@validate_card_object +def usageGet(card, mode=None, offset=None): + """Return the card's network usage statistics. + + Args: + card (Notecard): The current Notecard object. + mode (string): The time period to use for statistics. Must be one of: + "total" for all stats since activation (default), + "1hour", "1day", "30day". + offset (int): The number of time periods to look backwards, based on the specified mode. + + Returns: + dict: The result of the Notecard request containing: + "seconds": Number of seconds in the analyzed period + "time": UNIX Epoch time of start of analyzed period (or activation time if mode="total") + "bytes_sent": Number of bytes sent by the Notecard to Notehub + "bytes_received": Number of bytes received by the Notecard from Notehub + "notes_sent": Approximate number of notes sent by the Notecard to Notehub + "notes_received": Approximate number of notes received by the Notecard from Notehub + "sessions_standard": Number of standard Notehub sessions + "sessions_secure": Number of secure Notehub sessions + + Note: + Usage data is updated at the end of each network connection. If connected in continuous mode, + usage data will not be updated until the current session ends. + See: https://dev.blues.io/notecard/notecard-walkthrough/low-bandwidth-design#measuring-data-usage + """ + req = {"req": "card.usage.get"} + if mode: + req["mode"] = mode + if offset is not None: + req["offset"] = offset + return card.Transaction(req) + + +@validate_card_object +def usageTest(card, days=None, hours=None, megabytes=None): + """Test and project data usage based on historical usage patterns. + + Args: + card (Notecard): The current Notecard object. + days (int): Number of days to use for the test. + hours (int): If analyzing a period shorter than one day, the number of hours to use for the test. + megabytes (int): The Notecard lifetime data quota (in megabytes) to use for the test. Default: 1024. + + Returns: + dict: The result of the Notecard request containing: + "max": Days of projected data available based on test + "days": Number of days used for the test + "bytes_per_day": Average bytes per day used during the test period + "seconds": Number of seconds in the analyzed period + "time": UNIX Epoch time of device activation + "bytes_sent": Number of bytes sent by the Notecard to Notehub + "bytes_received": Number of bytes received by the Notecard from Notehub + "notes_sent": Number of notes sent by the Notecard to Notehub + "notes_received": Number of notes received by the Notecard from Notehub + "sessions_standard": Number of standard Notehub sessions + "sessions_secure": Number of secure Notehub sessions + + Note: + See: https://dev.blues.io/notecard/notecard-walkthrough/low-bandwidth-design#projecting-the-lifetime-of-available-data + """ + req = {"req": "card.usage.test"} + if days is not None: + req["days"] = days + if hours is not None: + req["hours"] = hours + if megabytes is not None: + req["megabytes"] = megabytes + return card.Transaction(req) + + +@validate_card_object +def wifi(card, ssid=None, password=None, name=None, org=None, start=None, text=None): + """Set up a Notecard WiFi to connect to a Wi-Fi access point. + + Args: + card (Notecard): The current Notecard object. + ssid (string): The SSID of the Wi-Fi access point. Use "-" to clear an already set SSID. + password (string): The network password of the Wi-Fi access point. + Use "-" to clear an already set password or to connect to an open access point. + name (string): Custom name for the SoftAP (software enabled access point). + Default is "Notecard". Use "-" suffix to append MAC address digits. + org (string): If specified, replaces the Blues logo on the SoftAP page with the provided name. + start (bool): Set to True to activate SoftAP mode on the Notecard programmatically. + text (string): String containing an array of access points in format: + '["FIRST-SSID","FIRST-PASSWORD"],["SECOND-SSID","SECOND-PASSWORD"]' + + Returns: + dict: The result of the Notecard request containing: + "secure": Boolean indicating if Wi-Fi access point uses Management Frame Protection + "version": Silicon Labs WF200 Wi-Fi Transceiver binary version + "ssid": SSID of the Wi-Fi access point + "security": Security protocol the Wi-Fi access point uses + + Note: + Updates to WiFi credentials cannot occur while Notecard is in continuous mode. + Change to periodic or off mode first using hub.set. + See: https://dev.blues.io/guides-and-tutorials/notecard-guides/connecting-to-a-wi-fi-access-point/ + """ + req = {"req": "card.wifi"} + if ssid: + req["ssid"] = ssid + if password is not None: + req["password"] = password + if name: + req["name"] = name + if org is not None: + req["org"] = org + if start is not None: + req["start"] = start + if text: + req["text"] = text + return card.Transaction(req) + + +@validate_card_object +def wirelessPenalty(card, reset=None, set=None, rate=None, add=None, max=None, min=None): + """View the current state of a Notecard Penalty Box, manually remove from penalty box, or override defaults. + + Args: + card (Notecard): The current Notecard object. + reset (bool): Set to True to remove the Notecard from certain types of penalty boxes. + set (bool): Set to True to override the default settings of the Network Registration Failure Penalty Box. + rate (float): The rate at which the penalty box time multiplier is increased over successive retries. + Default: 1.25. Used with set argument. + add (int): The number of minutes to add to successive retries. Default: 15. Used with set argument. + max (int): The maximum number of minutes that a device can be in a Network Registration Failure + Penalty Box. Default: 4320. Used with set argument. + min (int): The number of minutes of the first retry interval of a Network Registration Failure + Penalty Box. Default: 15. Used with set argument. + + Returns: + dict: The result of the Notecard request containing: + "minutes": Time since the first network registration failure + "count": Number of consecutive network registration failures + "status": If in a Penalty Box, provides associated Error and Status Codes + "seconds": If in a Penalty Box, number of seconds until the penalty condition ends + + Warning: + Misuse of this feature may result in the cellular carrier preventing future connections + or blacklisting devices for attempting to connect too frequently. + + Note: + See: https://dev.blues.io/guides-and-tutorials/notecard-guides/understanding-notecard-penalty-boxes + """ + req = {"req": "card.wireless.penalty"} + if reset is not None: + req["reset"] = reset + if set is not None: + req["set"] = set + if rate is not None: + req["rate"] = rate + if add is not None: + req["add"] = add + if max is not None: + req["max"] = max + if min is not None: + req["min"] = min + return card.Transaction(req) diff --git a/test/fluent_api/test_card.py b/test/fluent_api/test_card.py index bc05621..8ccd7e7 100644 --- a/test/fluent_api/test_card.py +++ b/test/fluent_api/test_card.py @@ -209,6 +209,141 @@ 'on': True, 'off': False } + ), + ( + card.monitor, + 'card.monitor', + { + 'mode': 'green', + 'count': 5, + 'usb': True + } + ), + ( + card.motion, + 'card.motion', + { + 'minutes': 10 + } + ), + ( + card.motionMode, + 'card.motion.mode', + { + 'start': True, + 'stop': False, + 'seconds': 30, + 'sensitivity': 0, + 'motion': 5 + } + ), + ( + card.motionSync, + 'card.motion.sync', + { + 'start': True, + 'stop': False, + 'minutes': 60, + 'count': 10, + 'threshold': 0 + } + ), + ( + card.motionTrack, + 'card.motion.track', + { + 'start': True, + 'stop': False, + 'minutes': 120, + 'count': 15, + 'threshold': 0, + 'file': '_motion.qo', + 'now': True + } + ), + ( + card.restart, + 'card.restart', + {} + ), + ( + card.restore, + 'card.restore', + { + 'delete': True, + 'connected': False + } + ), + ( + card.sleep, + 'card.sleep', + { + 'on': True, + 'off': False, + 'seconds': 60, + 'mode': 'accel' + } + ), + ( + card.trace, + 'card.trace', + { + 'mode': 'on' + } + ), + ( + card.triangulate, + 'card.triangulate', + { + 'mode': 'wifi,cell', + 'on': True, + 'usb': True, + 'set': True, + 'minutes': 30, + 'text': '+CWLAP:(4,"Blues",-51,"74:ac:b9:12:12:f8",1)\n', + 'time': 1606755042 + } + ), + ( + card.usageGet, + 'card.usage.get', + { + 'mode': '1day', + 'offset': 5 + } + ), + ( + card.usageTest, + 'card.usage.test', + { + 'days': 7, + 'hours': 12, + 'megabytes': 500 + } + ), + ( + card.wifi, + 'card.wifi', + { + 'ssid': 'MyNetwork', + 'password': 'MyPassword', + 'name': 'ACME Inc', + 'org': 'ACME Inc', + 'start': True, + 'text': '["SSID1","PASS1"],["SSID2","PASS2"]' + } + ), + ( + card.wirelessPenalty, + 'card.wireless.penalty', + { + 'reset': True, + 'set': False, + 'rate': 2.0, + 'add': 10, + 'max': 720, + 'min': 5 + } ) ] )