mocreo-smart-system
MOCREO Smart System Skill
Before using this sub-skill directly, check the root router skill at skills/mocreo-api/SKILL.md and follow its routing rules first.
Triggering
- When users ask about MOCREO Smart System devices (H5Pro, H6Pro, NS1/NS2/NS3, etc.).
- When users need account operations, asset management, device monitoring, history queries, or data export.
Environment (AI Handles Automatically)
Dependencies: If a script fails with ModuleNotFoundError, do not install packages silently.
pip install -r requirements.txt
Before any install attempt, tell the user which command you want to run and ask for permission because it may modify the current Python environment or a global environment when no virtual environment is active.
If a virtual environment is already active, prefer installing there after the user agrees.
If no virtual environment is active, explicitly warn that the install may go to the current or global Python environment and ask again before proceeding.
If the user does not approve, stop and provide the exact manual install command instead.
Try pip3 or python -m pip install requests python-dotenv only after the user has agreed to dependency installation.
Credentials:
- Do not proactively read
.env. Start with the auth resolver that matches the task, usuallypython skills/mocreo-smart-system/scripts/v3_resolve_auth.py --policy ...from the repository root. If the resolver orv3_login.pyexits with code2and stderr containsMOCREO_CREDENTIALS_MISSING, output the fixed "Credential Missing" response defined in the rootSKILL.mdverbatim and wait for the user to confirm setup is complete before continuing. - The bootstrap identifies the platform by guided questions about the app, hub model, or sensor family. It uses this mapping:
MOCREO Smart App=MOCREO Smart System=MOCREO V3- V3 hubs:
H3,H5-Lite,H5-Pro,H6-Lite,H6-Pro - V3-only sensors:
MS2,LS1,LS2,LS3,LW1,LD1,LB1,NS1,NS2,NS3 - Shared sensors needing a follow-up question:
ST5,ST6,ST8,ST9,ST10,MS1,SW2
- Store shared login credentials in the repo-root
.envusingMOCREO_USER,MOCREO_PASS, andMOCREO_PLATFORM. Store Smart System API keys in the local asset-scoped registry.mocreo_v3_apikeys.json, grouped byasset_idand permission profile. Create that registry file automatically the first time a Smart System API key is saved. - Password entry must happen in the terminal with hidden input. Never ask the user to type a password in chat or manually edit
.envunless they explicitly want to. - Treat
MOCREO_PLATFORM=smartas the routing hint only after the root router has selected this sub-skill. Remember that Smart System can also contain sensors, so generic sensor-data requests do not imply Sensor System. - If login fails after credentials were found, treat it as a configured-but-invalid state rather than a missing-setup state. Tell the user the saved account, password, or selected platform may be wrong, and ask them to rerun the bootstrap with the correct platform or update
.envdirectly.
Token lifecycle:
- After login, save both
access_tokenandrefresh_tokenfrom the JSON output. - If any API call returns 401: automatically run
python skills/mocreo-smart-system/scripts/v3_refresh_token.pyfrom the repository root with the saved tokens, then retry the original call. - If refresh also fails: inform the user the session has expired and re-run login.
Output Contract
- stdout: JSON data on success (parse this for chaining)
- stderr: Error messages only
- exit code:
0= success,1= failure
Script Location
All Smart System scripts are in skills/mocreo-smart-system/scripts/. Always run from the repository root:
python skills/mocreo-smart-system/scripts/v3_resolve_auth.py --policy asset-read --asset_id <ASSET_ID>
Timestamp Format
All --start and --end use millisecond Unix timestamps:
python -c "import time; print(int(time.time()*1000))" # now
python -c "import time; print(int((time.time()-86400)*1000))" # 24h ago
Instructions
- Atomic Operations First: All tasks must use the existing Smart System scripts. Prefer the resolver and atomic scripts that already exist in this folder before considering any new helper code.
- Standard Flow:
- Identify the operation type first:
token-only,asset-read,asset-write, orexport - Resolve auth with
v3_resolve_auth.pybefore calling the business script for that operation - List assets (
v3_list_assets.py) only when you still need to discover Asset ID - List devices (
v3_list_devices.py) only when you still need to discover Device ID - Execute the target atomic script with the resolved credential and any required IDs
- Before presenting timestamps or units to the user, fetch the asset display context and use the formatter helpers instead of ad hoc shell commands
- Identify the operation type first:
- Authentication Modes:
- Management scripts (user login, refresh, asset list, API key create/list/delete) use the
token-onlypolicy. - Public asset/device business APIs are documented as
X-API-KeyAPIs. In this skill, those scripts accept either--auth <TOKEN>or--auth <API_KEY> --apikey. - Asset-scoped read operations use the
asset-readpolicy: first resolve a saved read-capable key for that asset, then fall back to login token only when needed. - Asset-scoped mutating operations use the
asset-writepolicy: first resolve a saved write-capable key for that asset, then fall back to login token only when needed. v3_export_device_history.pyis the explicit exception: use theexportpolicy, which is token-first.- Use
v3_resolve_auth.pyrather than open-coding the key lookup order in chat or in one-off shell snippets. - Do not silently create a new API key just to satisfy that preference. If no suitable saved API key is available in the local registry for the selected asset, fall back to Bearer Token unless the user explicitly asks to create or save an API key.
- A
v3_resolve_apikey.pyresult of{"found": false}is a normal cache miss, not a user-facing error. Continue with Bearer Token fallback without describing that branch as a failure. - Remember that API keys are asset-bound. Resolve them by
asset_id, and prefer a key whose saved permission profile matches the requested operation. - For supported asset-scoped routes, the expected order is: resolve auth policy -> try the resolved credential -> only then fall back to Bearer Token in the allowed fallback case.
- Export is a documented exception to that order: for
v3_export_device_history.py, start with Bearer Token instead of API Key until the backend export bug is fixed. - Common mappings:
GET /v1/assets->token-onlyGET /v1/assets/{assetId}->asset-readPATCH /v1/assets/{assetId}->asset-writePATCH /v1/assets/{assetId}/config->asset-write- Read-only device status, signal, and history routes ->
asset-read - Device or asset rename/update routes ->
asset-write
- If an API-key-backed request returns
403with a permission message such asForbidden: API Key does not have the required permissions, tell the user the API key is valid but lacks the required permission for that operation. Do not describe that case as bad credentials, missing setup, or an expired session. - If a token-backed management request such as API key creation, listing, or deletion returns
403, tell the user their signed-in account lacks sufficient role permissions on that asset for that management action. Do not describe that case as a bad login, expired token, or missing API key. - Use these response patterns for permission-denied cases:
- API key permission failure:
The API key is valid, but it does not have enough permission for this action. This operation requires a write-capable permission on the selected asset or device, and the current key does not include it. - Account role permission failure:
Your account does not have sufficient permission on this asset to perform this management action. This is an asset-role restriction, not a login or token issue. Please contact the asset creator or administrator to elevate your asset-management permission.
- API key permission failure:
- If an API-key-backed request returns an unexpected server-side failure such as
500 Internal Server Error, retry that same asset-scoped request once with Bearer Token before concluding the operation failed. Explain that the API-key path for that route appears unsupported or unstable for the current server behavior.
- Management scripts (user login, refresh, asset list, API key create/list/delete) use the
- Mutating Operations Policy:
- Treat
v3_create_apikey.py,v3_delete_apikey.py,v3_update_asset.py,v3_update_asset_config.py,v3_update_device_name.py, andv3_export_device_history.pyas side-effecting operations. - Before changing anything, first read the current state with
v3_get_asset_details.py,v3_list_devices.py, orv3_get_device_details.py. - After every successful change, read the resource again or report the returned value so the user can see the final state.
- For testing or temporary operations, restore the original value in the same session whenever practical.
- Treat
- Asset Config Guardrails:
- Only send fields the user explicitly asked to change.
- Prefer small partial payloads instead of resubmitting the whole config object.
- The public OpenAPI currently documents
cityas the canonical example field forv3_update_asset_config.py. - Safe default fields are
city, and only other minimal fields already proven to work in practice such astz,country, andstate. - Do not send
timeFormatby default. The API may reject it. - Do not resend the full
configblock unless absolutely necessary. - Prefer
v3_update_asset_config.py --safeunless the user clearly needs a broader payload.
- API Key Safety Rules:
- For asset-scoped reads and writes, assume API Key is the default credential when a valid saved key for that asset is available in the local registry.
- Only create an API key when the user explicitly asks for one or when a temporary test requires it.
- Use the narrowest permissions possible. Prefer
device.readunless the user clearly needs write access. - Relevant documented permissions include
asset.read,asset.update,device.read, anddevice.update. - The full API key is only returned once. If you create one for immediate use, capture it from the create response and use it right away.
- These scripts run in a non-interactive shell. Never rely on their built-in terminal prompts — resolve all decisions in chat first, then pass the appropriate flags.
- Creating a key: Save the returned key into the local asset-scoped registry with the selected
asset_id, permission list, and derived tier. - Deleting a key: Confirm with the user in chat before running. Always pass
--forceso the script does not prompt. - The local registry can keep multiple asset-specific keys at the same time. Do not collapse them back into a single global API key field.
- When creating a new key, save it into the local asset-scoped registry with its
asset_id, permission list, and derived tier such asreadorwrite. - Temporary test keys must be deleted in the same session with
v3_delete_apikey.py. - API keys are asset-bound and cannot be reused across assets.
- Respect documented rate limits for API-key traffic: at most 1000 requests per hour and at most 3 concurrent requests per key.
- Read-only permissions such as
device.readare not sufficient for write operations such as renaming a device. Those operations require a write-capable permission such asdevice.update. - API key management itself is a privileged asset-management action. Even with a valid account token, the signed-in user may still be forbidden from creating, listing, or deleting API keys on an asset they do not administratively control.
- Use these standard success-message patterns for API key lifecycle events:
- First key saved for an asset:
Created a new API key for [Asset Name] and saved it in the local asset-scoped key registry. This is the first saved key for that asset in your local registry. - Additional key saved for the same asset:
Created a new API key for [Asset Name] and saved it in the local asset-scoped key registry. This asset already had saved keys, and the new key was added alongside them instead of replacing them. - First read-tier key saved for an asset:
Created and saved a read-capable API key for [Asset Name]. This key can be used for asset-scoped read operations supported by its permission set. - First write-tier key saved for an asset:
Created and saved a write-capable API key for [Asset Name]. This key can be used for asset-scoped update operations supported by its permission set. - Additional read-tier key saved for an asset:
Created and saved another read-capable API key for [Asset Name]. Your local registry now keeps more than one saved key for this asset. - Additional write-tier key saved for an asset:
Created and saved another write-capable API key for [Asset Name]. Your local registry now keeps more than one saved key for this asset. - First key saved for a different asset:
Created a new API key for [Asset Name] and saved it in the local asset-scoped key registry. This does not replace keys saved for other assets. - Registry auto-created:
The local Smart System API key registry did not exist yet, so it was created automatically when this key was saved. - Key deleted successfully:
Deleted the API key successfully and removed its saved record from the local asset-scoped key registry.
- First key saved for an asset:
- When reporting API key creation success, tell the user whether the key was saved as a read-tier or write-tier key, whether it was the first saved key for that asset, and whether the local registry file had to be created automatically.
- When reporting API key deletion success, tell the user that the key was removed both from the server-side asset and from the local asset-scoped registry when that local record existed.
- Signal and Export Interpretation:
v3_get_device_signal.pymay returnsuccess=truewithresult=null; do not treat that alone as a script failure.- If signal data is needed, prefer LoRa or hub-connected devices such as
LS1over Wi-Fi-only devices. v3_export_device_history.pytriggers a real export action. Use it only when the user explicitly asks to export or email data.- For now, always call
v3_export_device_history.pywith Bearer Token by default. Do not prefer API Key first for export. - The public API documents export as sending a download link to the specified email. When export succeeds, report the returned download URL and mention the destination email used.
- The export API is time-range-based, not record-count-based. Do not describe it as exporting an exact number of rows unless the API has already proven that behavior for the same request shape.
- If the user asks for "latest N records" and also wants an export email, first use
v3_get_device_history.pywith--limit Nto identify the target records, then export the time range covering them. Explain that the exported file may contain a slightly different row count because export usesfrom/toboundaries rather thanlimit. - When converting "latest N records" into an export window, add a small safety buffer before the oldest selected timestamp and after the newest selected timestamp instead of using exact boundaries, to reduce the chance of dropping a row at either export boundary.
- Default Selection Strategy:
- If the user does not specify an asset, list assets first and select the most relevant owned asset based on the request.
- If the user does not specify a device, list devices under the chosen asset and match by display name, model, or device type.
- Do not guess a destructive target. If multiple assets or devices are plausible for a mutating request, ask a short clarifying question.
- User-Facing Formatting Rules:
- Before presenting temperature readings, timestamps, or export expectations to the user, fetch the asset display context with
v3_get_asset_display_context.pyand use that output as the display source of truth when available. - Use the returned
tzas the default timezone for user-facing timestamps unless the user explicitly asked for a different timezone. - If
config.timeFormatishour12, present times in a familiar 12-hour local format such as02/26/2026 08:58:20 AM. If it ishour24, present times in a familiar 24-hour local format such as2026-02-26 08:58:20. - Format timestamps with
v3_format_timestamps.pyrather than ad hocpython -cone-liners. If the formatter returnssuccess=false, do not retry with improvised commands; present UTC or raw timestamps and explain that local timezone formatting was unavailable on the current machine. - For temperature data, prefer the normalized
temperatureUnitobject fromv3_get_asset_display_context.pyrather than inferring from raw terminal glyphs. - Treat clear values such as
C,F,°C,°F,℃, or℉as trustworthy unit settings. - If terminal output garbles the unit glyph, inspect the underlying Unicode value before treating it as invalid.
- Normalize unit glyphs by code point first, not by terminal appearance. In particular,
\u2103means℃and\u2109means℉, even if the terminal renders a different character. - Treat visually corrupted terminal renderings such as
沈or⊥as display artifacts, not as authoritative unit values, until the underlying Unicode value has been checked. - Never infer
°For°Cfrom the numeric temperature value alone. If the normalized unit is still unreadable, tell the user the asset's temperature-unit setting could not be read cleanly from the server response instead of guessing. - If you convert temperature values for display, say which unit you are showing.
- Before presenting temperature readings, timestamps, or export expectations to the user, fetch the asset display context with
- History Query Rules:
- For
v3_get_device_history.py, always providefrom,to,tz, andfield. - Valid
fieldvalues aretemperature,humidity,water_leak,water_level, andfrozen. - Optional
windowDurationvalues use compact duration strings such as1m,30m,1h,1d, or1mo. - Optional
aggregationsTypevalues are comma-separated combinations ofmean,max, andmin, and only make sense whenwindowDurationis provided. limitis only for constrained result sets and should stay within1-10000.- Do not combine
limitwith aggregation parameters unless the API behavior is clearly documented for that combination. - Do not assume
limit=1means "the latest reading". Treat the history endpoint as potentially returning data in ascending time order unless the API has explicitly proven otherwise. - When the user asks for the latest
Nreadings, estimate a narrow recent time window first instead of querying a very large range. Use a default reporting-cadence assumption of one reading every 10 minutes unless the device behavior has already shown a different cadence. - For latest-reading requests, start with a heuristic window such as
max(6 hours, N * 10 minutes * 12)and request enough rows to cover that window comfortably. - After the data is returned, sort the records by
timein ascending order yourself and take the finalNrows as the latest readings. - If the initial window does not return enough rows, automatically widen the time range and retry before telling the user the data is missing.
- Policy-Driven Entry Rules:
- Do not treat
v3_get_device_details_auto.pyorv3_get_device_history_auto.pyas the primary entry for new work. - For new Smart System tasks, prefer
v3_resolve_auth.pyplus the existing atomic business script for the route you need. - Think in policies, not endpoint-specific wrappers:
- asset or device reads -> resolve
asset-read, then call the read script - asset or device writes -> resolve
asset-write, then call the write script - token-only management routes -> resolve
token-only, then call the management script - export -> resolve
export, then call the export script
- asset or device reads -> resolve
- The older
*_auto.pyscripts are compatibility shortcuts for common read-only queries, not the preferred design surface for future expansion.
- Export Query Rules:
- For
v3_export_device_history.py, always provideemail,from,to,tz, andfields. - Valid
fieldsvalues are comma-separated combinations oftemperature,humidity,water_leak,water_level, andfrozen. - Optional export aggregation uses the same
windowDurationandaggregationsTyperules as history queries. - Use the user's explicitly requested email when provided; otherwise use the confirmed default email for the current account.
- Do not present
v3_export_device_history.pyas supportinglimit; it does not. - For requests framed as "export the latest N records", tell the user you are exporting the time range that covers those records, not guaranteeing an exact row count in the exported file.
- Although the script accepts
--apikey, the current backend export path has a known API-key bug. For now, use Bearer Token as the default and practical credential for export on every asset unless the backend behavior changes.
Scripts
User & Auth (3)
v3_login.py:--email--password-> full JSON (access_token + refresh_token)v3_refresh_token.py:--token--refresh_token-> new token JSONv3_resolve_auth.py:--policy[--asset_id] [--permissions] -> short-circuit auth resolution fortoken-only,asset-read,asset-write, orexport
API Keys (4)
v3_create_apikey.py:--token--asset_id--name--permissions[--expires_at] [--asset_name] -> new key infov3_list_apikeys.py:--token--asset_id-> all API keysv3_delete_apikey.py:--token--asset_id--prefix[--force] -> delete key by prefixv3_resolve_apikey.py:--asset_id[--permissions] [--tier] -> best saved API key record for that asset from the local registry
Assets (5)
v3_list_assets.py:--token-> all assetsv3_get_asset_details.py:--auth--asset_id[--apikey] -> asset configv3_get_asset_display_context.py:--auth--asset_id[--apikey] -> normalized asset display metadata including timezone, time format, and trusted temperature-unit analysisv3_update_asset.py:--auth--asset_id--name[--apikey] -> rename assetv3_update_asset_config.py:--auth--asset_id--config[--apikey] [--safe] -> update timezone, city, etc.
Devices & Monitoring (8)
v3_list_devices.py:--auth--asset_id[--apikey] -> all devicesv3_get_device_details.py:--auth--asset_id--device_id[--apikey] -> real-time status (temp, battery, online)v3_get_device_details_auto.py:--asset_id--device_id-> compatibility shortcut for read-only device details; preferv3_resolve_auth.py+v3_get_device_details.pyfor new flowsv3_update_device_name.py:--auth--asset_id--device_id--name[--apikey] -> rename devicev3_get_device_signal.py:--auth--asset_id--device_id[--apikey] -> signal strength with gatewayv3_get_device_history.py:--auth--asset_id--device_id--start--end--tz--field[--window] [--agg] [--limit] [--apikey] -> historical datav3_get_device_history_auto.py:--asset_id--device_id--start--end--field[--limit] [--window] [--agg] [--tz] -> compatibility shortcut for read-only history; preferv3_resolve_auth.py+v3_get_device_history.pyfor new flowsv3_export_device_history.py:--auth--asset_id--device_id--email--start--end--tz--fields[--window] [--agg] [--apikey] -> export to email
Helpers (3)
v3_auth_policy_helpers.py: internal shared auth-policy helper used by resolver and compatibility shortcutsv3_query_read_helpers.py: backward-compatible wrapper aroundv3_auth_policy_helpers.pyv3_format_timestamps.py:--tz--time_format--timestamps <TS...>-> formatted user-facing timestamps with graceful fallback when timezone data is unavailable
Example Workflow
# Resolve token-only auth for management routes such as asset listing
python skills/mocreo-smart-system/scripts/v3_resolve_auth.py --policy token-only
# Resolve read auth for an asset-scoped read route
python skills/mocreo-smart-system/scripts/v3_resolve_auth.py \
--policy asset-read --asset_id <ASSET_ID> --permissions device.read
# Resolve write auth for an asset-scoped mutation
python skills/mocreo-smart-system/scripts/v3_resolve_auth.py \
--policy asset-write --asset_id <ASSET_ID> --permissions asset.update
# Resolve export auth for export routes (token-first)
python skills/mocreo-smart-system/scripts/v3_resolve_auth.py \
--policy export --asset_id <ASSET_ID>
# Then call the atomic business script with the resolved auth value
# and add --apikey only when the resolver says apikey=true.
More from mocreo-iot/skills
mocreo-api
MOCREO English device-data router for battery, temperature, humidity, online status, alerts, and history queries by device ID, node ID, asset, or hub across Smart System (V3) and Sensor System (V2).
10mocreo-sensor-system
MOCREO Sensor System skill for battery, status, alerts, node data, and monitoring queries on V2 hubs and sensor nodes.
1