Skip to content

Inficold Integration Guide

This guide explains how to connect Inficold cold rooms to Coldtivate. It covers:

  • System administrators: setup, configuration and troubleshooting.
  • Developers: how the integration works in the Base API and the mobile app.

For how sensors work in general, see Integrations & Data Collection.


1. How the integration works

Inficold devices report their readings to Inficold's own cloud. Coldtivate never connects to the devices themselves. It uses two Inficold interfaces, each for one job:

Interface Used for Credentials
Inficold dashboard (infilog.com/devicedashboard) Finding which devices a user's Inficold account can see. Used only while a sensor is being set up. The user's own dashboard username and password, typed in the app. Used once and never stored.
Official history API (infilog.com/api/coldtivate/get_scr_data.php) Checking the selected device and its chambers, and reading temperature and humidity. One Coldtivate bearer token, set on the server (INFICOLD_API_TOKEN). Never sent to the app.

The official API is a minimal, read-only endpoint Inficold built for Coldtivate. Its published spec (Inficold Sensor Data API – Coldtivate Integration) only covers reading the history of one device for a date range:

  • Request: id (device ID), date and optional end_date (YYYY-MM-DD), and optional limit.
  • Response: status, message and data, where each record has unit_id, log_id, timestamp, temperature_chamber_1/_2 and humidity_chamber_1/_2.

It can't list devices, filter by time of day, report a timezone or push readings. That's why devices are found through the dashboard, each poll downloads whole local days, timestamps fall back to Asia/Kolkata, and readings are polled. The adapter also relies on fields the spec doesn't include (see Polling and data).

Step What happens
1 Inficold installs the device, connects it to its cloud and enables it for Coldtivate's API token.
2 A Registered Employee chooses Inficold in the app and signs in with their Inficold dashboard account.
3 The Base API logs into the dashboard and returns the account's devices by name and ID. It makes no API calls yet.
4 The user picks a device and presses Test connection. The Base API checks that one device live through the official API: is it authorized, and which chambers does it have?
5 The user picks a chamber and saves the cooling unit.
6 Every 4 hours, the Base API polls the official API for each linked chamber and stores its temperature and humidity. It never uses the dashboard for this.
7 The readings appear in the app under Cooling Units → Room Conditions.

Key facts:

  • One chamber is one cooling unit. A device has one to three chambers, and each chamber is identified as <device_id>:<chamber>, for example 186:2. Users never type this.
  • Dashboard credentials are transient. They're used for one discovery request and are never saved, logged or sent back to the app.
  • A device must pass both checks. It must be visible in the user's dashboard and accessible with Coldtivate's API token. A device that fails the second check is reported by name and ID when it's selected, so the user can give support the exact device.
  • Each request is bounded. Listing devices makes no API calls, and the connection test makes one, so large accounts never produce long requests.
  • Readings: temperature (°C) and relative humidity (%) are read for each chamber.
  • History: when a chamber is linked, the last 24 hours are imported. Older history isn't imported.

2. Requirements

Devices and accounts

Coldtivate has no hardware setup step. Before a company can link devices, confirm with Inficold that:

Item Why
The customer has an Inficold dashboard account that lists their devices. The account is how Coldtivate finds the customer's devices.
Each device is enabled for Coldtivate's API token. Selecting a device the token can't read shows its name and ID with a "contact support" message.
The device timezone is known. Inficold timestamps carry no timezone. The default is Asia/Kolkata.
Each device is online and reporting. A device that reports nothing appears as a sensor with no data.

Network

  • Outbound access: the Base API web and Celery worker containers need outbound HTTPS access to infilog.com, for both the dashboard and the history API. No inbound connection from Inficold is needed.
  • Celery: the worker and beat must be running. Beat queues sensor polling every 4 hours.
  • Redis: the Base API cache must be available. Device details (name, chamber count) are cached there.

Settings

Only values that differ between deployments are environment variables on the Base API:

Variable Default Purpose
INFICOLD_API_TOKEN (empty) Secret. Coldtivate's bearer token for the official API. Required for discovery, testing, saving and polling. If it's empty, Inficold setup and polling fail with "Inficold API token is not configured", and the rest of the platform is unaffected. Set it per environment (development, staging, production).
INFICOLD_API_URL https://infilog.com/api/coldtivate/get_scr_data.php Official history endpoint. Configurable so mock-server and staging stress runs can point elsewhere.

Stable provider URLs and implementation defaults are constants in the code, following the other sensor providers:

Constant Value Purpose
DASHBOARD_LOGIN_URL (inficold.py) https://infilog.com/devicedashboard/index.php Dashboard login page.
DASHBOARD_URL (inficold.py) https://infilog.com/devicedashboard/index.php?md=User&sd=Dashboard Dashboard page the devices are read from.
DASHBOARD_TIMEOUT_SECONDS (inficold.py) 10 Timeout for each dashboard request.
DEFAULT_TIMEZONE (inficold.py) Asia/Kolkata Timezone used to read Inficold timestamps.
METADATA_CACHE_TTL_SECONDS (inficold.py) 24 hours How long cached device details are used without refreshing.
METADATA_STALE_TTL_SECONDS (inficold.py) 7 days How long cached device details may still be used if Inficold is briefly unreachable.
INFICOLD_DISCOVERY_TOKEN_MAX_AGE_SECONDS (inficold.py) 15 minutes Lifetime of each discovery ticket: from signing in to testing a device, and from the test to saving the cooling unit.

Limitation: the design supports one Inficold API token per deployment. Supporting several Inficold accounts with different tokens would need a mapping from devices or companies to tokens.


3. Configuration

Step 1: set the API token

Set INFICOLD_API_TOKEN through the environment's normal secret mechanism, then restart the Base API web and Celery containers. Nothing is configured in the Django admin.

In the mobile app, as a Registered Employee of the company:

  1. Create or edit the cooling unit.
  2. Turn on Sensor available and choose inficold.
  3. Enter the Inficold dashboard username and password, then press Confirm.
  4. The app lists the account's devices by name and ID, for example Panposh 20MT Rourkela / ID 186. Use the search box to filter them, then select one.
  5. Press Test connection. The app checks the device against Inficold, showing Testing connection… while it runs. Testing never saves anything.
    • If it fails, the reason is shown and the device list stays open, for example "Panposh 20MT Rourkela (ID 186) isn't enabled for Coldtivate". Pick another device, or contact support with that ID.
    • If it succeeds, Connected. The device is reachable. is shown with the device's chambers.
  6. Select the chamber for this cooling unit and press Save, then save the cooling unit within 15 minutes of the test. Use the back arrow to test another device.

Repeat for each chamber, one cooling unit per chamber.

Step 3: verify

  1. Readings: open Cooling Units → Room Conditions for the cooling unit. Readings from the last 24 hours should appear right after saving. Check that the times match the room's local clock.
  2. Polling: after the next scheduled poll (at most 4 hours later), in the Django admin under Storage → Sensor integrations, the integration should have a recent Last successful poll at and Polling error unchecked. Its Username is the device ID and its password is empty.
  3. Freshness: Last updated on the Room Conditions screen should keep advancing. If no new reading arrives for more than 12 hours, the screen shows a Stale warning.

Maintenance

  • Rotating the token: change INFICOLD_API_TOKEN and restart the containers. Every linked chamber uses the new token on its next poll, and polling history is kept.
  • Editing a cooling unit: changes that don't touch its sensor don't need the Inficold sign-in again.
  • Changing the chamber: choose Inficold again and sign in. The new chamber starts a fresh polling history.
  • Moving a cooling unit:
    • Same company: allowed.
    • Different company: rejected until the Inficold sensor is selected again (sign in and test the device again) or turned off.
  • Removing a device: turn off the sensor on each cooling unit that uses one of its chambers. A chamber whose device is no longer enabled for the token fails every poll and raises a sensor error notification.

4. Developer reference

Components

Component Location
Polling adapter and dashboard client base/apps/storage/services/sensors/inficold.py (InficoldIntegration, InficoldDashboardClient)
Discovery workflow: device and chamber discovery, discovery errors, signed tickets, connection test and save validation base/apps/storage/services/sensors/inficold.py (its own section, using plain user IDs)
Credential resolution for polling (generic; Inficold fills in the token through InficoldIntegration.runtime_credentials()) base/apps/storage/services/sensory.py
Adapter registry (applies an adapter's schemas only when it declares them) base/apps/storage/services/sensors/registry.py. Inficold's InficoldCredentials and InficoldConfiguration schemas live in inficold.py.
Polling task base/apps/storage/tasks/sensor_polling.py (sync_sensor_data, poll_sensor_integration)
Source-list and test-connection endpoints base/apps/storage/views/sensors.py (SensorIntegrationViewSet, InficoldViewSet)
Cooling-unit validation base/apps/storage/serializers/cooling_unit.py
Mobile sign-in, device and chamber steps src/screens/Dashboard/Management/AddCoolingUnit/components/Sensors/ (components/SensorModal/components/InficoldForm.tsx, like Ecozen's own form; the shared components/SourceList.tsx list; sensorErrors.ts for error messages and the expired-session recovery when saving)

Adapter contract

InficoldIntegration follows the shared adapter contract, and never touches Django models. It's built from a plain configuration:

{
    "type": "inficold",
    "username": "186",          # the device ID
    "password": "<API token>",  # from INFICOLD_API_TOKEN
    "source_id": "186:2",       # <device_id>:<chamber>
}
  • authorize(): sets the bearer header. username must be one positive device ID.
  • list_sources(): returns the chambers of that one device, using the API's chamber count.
  • get_datums_from_source(): reads temperature and humidity for the chamber. A source whose device differs from username is rejected.
  • validate_source(source_id, live=False): checks the chamber exists. live=True skips the cache, as saving does.
  • test_connection(): a live connection test for the configured device. One uncached request that returns the device's chambers, used by the chamber step.

At poll time, resolve_sensor_credentials() builds this configuration from the saved integration, with the token read from settings on every call.

Dashboard client

InficoldDashboardClient(username, password).discover() returns the account's devices as (id, name) pairs. For each call it:

  1. Opens a fresh requests.Session and loads the login page to get a session cookie.
  2. Posts md=UserLogin, sd=CheckUser, Username and Password without following redirects.
  3. Treats a redirect back to the login page, or a login form returned instead, as wrong credentials. A 302 alone isn't trusted.
  4. Loads the dashboard and reads devices from two places on the real page:
    • The device selector. The page builds it inside a script as a JavaScript string ('<option value="4101">Cold Room A</option>', inserted into <select name="DeviceID">). Only scripts that build that selector are read, and a plain-HTML selector is read too.
    • The sidebar. Each device is a treeview entry whose header is the device name, followed by ILDeviceID=<id> links.
    • Selector names win over sidebar names. Numeric options and IDs anywhere else on the page, such as map data, are ignored.
  5. Keeps positive IDs only, removes duplicates, and turns names into short plain text, falling back to Device <id>.
  6. Closes the session. The dashboard's logout endpoint is deliberately not called: the session exists only in memory for this one request.

Credentials, cookies and page HTML are never logged or stored. Only counts and error types are logged.

API endpoints

Discover devices: POST /storage/v1/user-sensor/sources/

{
  "integration_type": "inficold",
  "username": "<dashboard username>",
  "password": "<dashboard password>"
}
  • Access: any authenticated user, like the other providers' source listing. Whether the user may configure a given cooling unit is checked when it is saved.
  • Behaviour: dashboard login and device parsing only. No official API calls.
  • Success (200): the dashboard devices, and a device ticket for the connection test.
    {
      "sources": [
        {"id": "186", "name": "Panposh 20MT Rourkela"},
        {"id": "187", "name": "Second cold room"}
      ],
      "device_token": "<signed device ticket>"
    }
    
  • Errors: always {"code": ..., "detail": ...}.
Status code When
400 INFICOLD_DASHBOARD_AUTHENTICATION_FAILED Username or password missing.
401 INFICOLD_DASHBOARD_AUTHENTICATION_FAILED The dashboard rejected the credentials.
404 INFICOLD_NO_DASHBOARD_DEVICES The account lists no devices.
503 INFICOLD_PROVIDER_UNAVAILABLE Dashboard unreachable, an unexpected page, or the token isn't configured.

Test a device's connection: POST /storage/v1/inficold/test-connection/, next to Ecozen's POST /storage/v1/ecozen/test-connection/.

{"device_id": 186, "device_token": "<signed device ticket>"}
  • Access: any authenticated user, as for listing devices. The ticket limits which devices can be tested.
  • Behaviour: Inficold's connection test and chamber discovery in one operation, so nothing is saved.
    1. It checks that the device ticket was issued to this user and lists this device.
    2. It makes exactly one live official API request for the device, never served from cache (InficoldIntegration.test_connection()). That single response shows whether the token is accepted and the device is authorized, and gives its chamber count. It also refreshes the metadata cache that polling uses.
  • Success (200):
    {
      "success": "Successfully connected",
      "sources": [{"id": "186:1", "name": "Chamber 1"}, {"id": "186:2", "name": "Chamber 2"}],
      "source_token": "<signed source ticket>"
    }
    
Status code When
400 INFICOLD_DISCOVERY_EXPIRED / INFICOLD_DISCOVERY_INVALID The ticket expired, was tampered with, belongs to another user, is a source ticket, doesn't list this device, or the device ID is malformed. No API call is made.
409 INFICOLD_DEVICE_NOT_ENABLED Inficold doesn't give Coldtivate's token access to this device, or the token itself was rejected. The detail includes the device ID.
503 INFICOLD_PROVIDER_UNAVAILABLE Timeout, server error, or an inconsistent response such as a unit_id mismatch.

Not-enabled devices use 409 rather than 422, because the mobile HTTP client logs request details on 422.

Link a chamber: create or update the cooling unit (POST or PUT /storage/v1/cooling-units/) with:

{
  "sensor": true,
  "sensor_data": {
    "type": "inficold",
    "source_id": "186:2",
    "source_token": "<signed source ticket>"
  }
}

The saved integration has username = "186" and an empty password. The tickets and any dashboard credentials are never stored.

Read the sensor: GET /storage/v1/cooling-units/{id}/sensor-data/ returns source_id, type, username (the device ID) and the link date. It never returns a password.

Discovery tickets

Discovery uses two short-lived tickets, signed with Django signing. Each has its own salt, so one can never be used in place of the other. Both contain only the user ID and provider type, plus the signing timestamp. They aren't tied to a company: like with other providers, users can attach a device they control to any cooling unit they're allowed to configure.

Ticket Issued by Lists Accepted by
Device ticket (device_token, salt storage.inficold.device-ticket.v1) Listing devices The dashboard device IDs The connection test
Source ticket (source_token, salt storage.inficold.source-ticket.v1) The connection test That device's <device_id>:<chamber> sources Saving the cooling unit

Each ticket lasts 15 minutes (INFICOLD_DISCOVERY_TOKEN_MAX_AGE_SECONDS in inficold.py) from when it's issued, so the time to save starts at the connection test.

When a source ticket is required: adding Inficold to a cooling unit, changing its chamber, or saving it under a different company. Saving an unchanged chamber in the same company doesn't need one.

Checks on save:

  1. The signature is a valid source ticket and it's younger than 15 minutes.
  2. It was issued to the current user.
  3. The chosen chamber is one of the sources it lists. A guessed chamber, or a device ticket, is rejected without an API call.
  4. The user has a staff role in the cooling unit's company, which is the normal cooling-unit authorization.
  5. The device and chamber pass a live check against the official API.

Errors: ticket failures return sensor_data.source_token as INFICOLD_DISCOVERY_EXPIRED or INFICOLD_DISCOVERY_INVALID. The app then shows "Your Inficold session expired…" and reopens the Inficold sign-in. Device or chamber failures return sensor_data.source_id.

Polling and data

  • Schedule: sync_sensor_data runs every 4 hours and queues poll_sensor_integration for each linked chamber. Polling uses only the official API.
  • Poll window:
  • The first sync covers the last 24 hours.
  • After that, each poll requests everything since the chamber's last stored reading, minus a 15-minute overlap.
  • Fields beyond the published spec: the adapter requires these, and they aren't in Inficold's spec. Confirm them with Inficold if the API changes.
Field Used for
offset (request) Paging through results.
has_more (response, required) Knowing when to stop paging. A response without it is treated as malformed (provider unavailable).
returned (response) Sanity check on the page length. Only logged on mismatch.
chamber_count (response) Listing and validating chambers, from 1 to 3. The spec only describes chambers 1 and 2.
device_name (response) Cached device metadata.
timezone (response, optional) Reading timestamps. Falls back to Asia/Kolkata when absent.
  • Requests:
  • The API only filters by local date (date, end_date) and pages with limit and offset.
  • The connector fetches every page, up to 1,000 rows per page, with a 15-second timeout per request and a safety limit of 500 pages.
  • It then keeps only readings inside the exact time window.
  • Timestamps: Inficold sends naive local times. They're read in the device timezone if the API ever reports one, else Asia/Kolkata (DEFAULT_TIMEZONE in inficold.py), and stored in UTC.
  • Values: "NaN", missing and malformed values are skipped. A successful response with no rows is a successful poll.
  • Storage: readings are stored in SensorReading with the provider timestamp, and averaged into 15-minute buckets in CoolingUnitSpecifications (see Polling and ingestion).
  • Identity: changing the provider type or source ID resets the polling watermark. Rotating the token doesn't.
  • Failures:
API response Treated as Result
HTTP 401 Token rejected Non-retryable: the chamber fails at once and is reported once.
HTTP 403 / 404, or body status 403 / 404 Device not accessible Non-retryable. In the connection test, it's reported as INFICOLD_DEVICE_NOT_ENABLED.
Timeout, network error, 5xx, malformed JSON, unit_id mismatch, invalid chamber count or pagination Provider unavailable Retried after 5, 15 and 30 minutes, then SENSOR_ERROR and Sentry.
  • Device metadata: device name and chamber count are cached per device and token fingerprint (a hash, never the token itself), with a stale fallback for transient failures.

Tests

  • Unit and integration tests: the dashboard client and adapter tests live in services/sensors/test_inficold.py; discovery, tickets, saving and polling live with the general sensor tests in test_sensor_ingestion.py. Both use mocked HTTP, run with the normal test suite, and fail any test that attempts a real request.
  • Shared adapter contract: services/sensors/test_sensors.py runs the common adapter checks for Inficold offline, using a plain username/password configuration.
  • Live checks: opt-in with TEST_SENSORS_INFICOLD, plus TEST_SENSORS_INFICOLD_USERNAME (device ID), TEST_SENSORS_INFICOLD_PASSWORD (API token) and TEST_SENSORS_INFICOLD_SOURCE_ID. They read the device's full history, so don't run them against production. See Testing.

5. Troubleshooting

Symptom Likely cause Fix
App: "Incorrect Inficold username or password." The dashboard rejected the sign-in. Check the credentials by logging into the Inficold dashboard directly.
App: "No devices were found in this Inficold account." The account lists no devices. Ask Inficold to assign the devices to that account.
Test connection: " (ID ) isn't enabled for Coldtivate…" Coldtivate's token can't read that device, or the token was rejected. In the Base API logs, "rejected the deployment API token" means the token is wrong: update INFICOLD_API_TOKEN. Otherwise ask Inficold to enable that device ID for Coldtivate.
App: "Too many Inficold sign-in attempts. Please wait a while and try again." The Base API's per-user write rate limit (120 requests per minute) was reached (HTTP 429). Wait a minute before signing in or testing again.
App: "Inficold is temporarily unavailable. Please try again." Inficold unreachable, an unexpected dashboard page, or INFICOLD_API_TOKEN isn't set. Check the Base API logs for the error type, outbound HTTPS to infilog.com, and the token setting.
App: "Your Inficold session expired…" after Test connection or when saving More than 15 minutes passed since signing in (for the test) or since the test (for saving). Sign in to Inficold again and re-select the chamber.
Saving fails with "Inficold chamber N does not exist for this device" The device's chamber count changed since discovery. Choose Inficold again and pick an existing chamber.
Moving a cooling unit to another company reopens the Inficold sign-in with "Your Inficold session expired…" Saving under a different company needs a new source ticket. Sign in, test the device and pick the chamber again, or turn the sensor off before moving.
Readings are shifted by a fixed number of hours The devices aren't on Asia/Kolkata time. Ask the development team to change DEFAULT_TIMEZONE in inficold.py. New readings use it; stored readings keep their times.
Stale warning, or a sensor error notification Polls are failing, or the device has stopped reporting to Inficold. In the admin, check the integration's Polling error and Last successful poll at. Check the token is set and the device is still enabled, then confirm with Inficold that it's reporting.
No data, but no error Inficold returns no readings for that period. The device isn't sending data to Inficold. Contact Inficold.
Gaps in the graph after an outage The next successful poll fills gaps automatically, as long as Inficold has the data. Wait for the next poll. Readings Inficold received more than 15 minutes after later ones were stored can't be recovered.

6. Known limitations

  • Minimal official API: Inficold's spec only covers date-range history for one device. Paging, chamber count, device name and timezone aren't in it, so a change on Inficold's side could break polling without notice.
  • Undocumented dashboard: discovery depends on the dashboard's login flow and page markup, which Inficold doesn't document and may change. If it changes, discovery fails with "temporarily unavailable" while polling of already-linked chambers keeps working.
  • One token per deployment: every company uses the same Coldtivate API token.
  • Ownership: a device is offered to anyone who can sign in to a dashboard account that lists it. Two companies could link the same device.
  • Enablement is checked late: the device list comes from the dashboard alone, so a device that isn't enabled for Coldtivate only shows as such once it's selected.
  • No older history: only the last 24 hours are imported when a chamber is linked.
  • Late readings: a reading that reaches Inficold after newer readings have been stored, with a timestamp more than 15 minutes older, is never fetched.
  • Silent devices: an empty successful response is a normal poll, so a silent device looks the same as a period without readings. It's only caught by the 12-hour Stale warning.
  • Request volume: Inficold filters only by date, so each poll downloads the whole local day for the device. This is repeated for each of its chambers.
  • Pagination: it assumes Inficold's result order is stable while pages are fetched. This hasn't been confirmed by Inficold yet.