Adding Meters from New Acquisition Servers
info
- Recurring Task: Needed when Facilities installs a new Acquisuite, or replaces the meters attached to an existing one
- Frequency: Rare, roughly once a year or less
This page covers getting data flowing from a new Acquisuite. Once data is landing in the database, see Adding Meters / Buildings for the rest of the dashboard wiring.
note
Configuring the Acquisuite itself (network setup, pointing it at our API) is handled by Facilities or the installer, not by us. This page starts from the point where the device is already uploading.
How Acquisuite Data Reaches the Dashboard
- The Acquisuite POSTs a gzipped CSV log to
https://api.sustainability.oregonstate.edu/v2/energy/meter, authenticating with a shared password (ACQUISUITE_PASS) - The request is handled by
post()inbackend/app/meter.js, which identifies the meter by the address<SERIALNUMBER>_<MODBUSDEVICE>- If no meter with that address exists, one is created automatically in the
meterstable, using the name and device class the Acquisuite reported
- If no meter with that address exists, one is created automatically in the
- The device class is looked up in
meter_classes.jsto figure out which CSV column holds which reading - Readings are written to the
datatable. Only rows landing on a quarter hour (:00,:15,:30,:45) are kept
Steps 1, 2, and 4 need nothing from us. Step 3 is the only place a new acquisition server can get stuck, and it is where all the work below happens.
Step 1: Check Whether the Meter Class Is Already Known
meter_classes.js is keyed by the MODBUSDEVICECLASS value the Acquisuite reports for each attached device. Common meter models are already listed there, so a new device often works with no code change at all.
Find the new meters in the database:
SELECT * FROM meters WHERE address LIKE '001EC60565DF%';
Substitute the Acquisuite's serial number. Then check whether readings are actually arriving for one of them:
SELECT * FROM data WHERE meter_id = <id from above> ORDER BY time_seconds DESC LIMIT 10;
- Rows in
data— the class was already known and everything worked. Skip to Step 3 - A row in
meters, butdatais empty — the class is new and unmapped. Continue to Step 2
caution
This is the failure fingerprint to watch for. The meter row still gets created with a correct-looking name and class number, so at a glance nothing seems wrong. The class lookup fails on the next upload, and the API just returns FAILURE with no detail. You can confirm in the MeterPost log group in Cloudwatch.
Step 2: Add the New Meter Class
Get a log export
Download the device's CSV log from the Leviton/Obvius BMO portal, which the Acquisuites upload to alongside our API. This gives you the same column layout our endpoint receives.
Map the columns
Add an entry to meter_classes.js keyed by the reported MODBUSDEVICECLASS, mapping column index → point name:
8955: {
// Dixon Rec Center replacement electric meters (Acquisuite 001EC60565DF).
4: 'accumulated_real',
22: 'real_power',
// ...
},
- Compare the export column-by-column against an existing class for a similar model. New meters are frequently register-compatible with a device we already support, which makes the mapping a verification exercise rather than a guess
- The energy type shown on the dashboard is inferred from which points you map, not set explicitly —
totalmakes it Steam,cubic_feetmakes it Gas,accumulated_realmakes it Electricity - Leave a register unmapped if its values do not look plausible. An unmapped point simply does not appear on the dashboard, which is much better than charting garbage. Leave a comment explaining why, so the next person does not "fix" it
Deploy
meter_classes.js lives in backend/dependencies/, which is published as the EnergyModelLayer Lambda layer. Merging to master redeploys it automatically via the SAM deployment GitHub Action — there is no separate layer bump to do.
Once deployed, wait for the next upload and re-run the data query from Step 1 to confirm readings are landing.
Step 3: Wire the Meters into the Dashboard
New meters exist in the meters table but are not attached to any building yet. Follow Adding Meters / Buildings to set up meter_groups, meter_group_relation, and buildings.
If these meters replace existing ones
Do not delete the old meter — that would throw away its history. Instead, move it out of the building's default meter group and into a new, non-default group of its own:
- Delete the old meter's row from
meter_group_relationfor the building's default group - Create a new
meter_groupsentry named something likeDixon Electricity (Retired), withdefaultset to0 - Add a
meter_group_relationrow linking the old meter to that new group
The historical data stays viewable, but it no longer mixes into the building's main graph alongside the replacement meters.
Step 4: Verify
- Confirm the new graphs render on the dashboard
- Check that a full day of data has accumulated with no gaps
- Add the retired meter to
blacklist.jsonin the check-acq automated job, so it does not generate outage alert emails forever
Reference: Dixon Rec Center (2026)
Both cases showed up in the same install, which makes it a useful example:
- Steam meter — reported class
4444(Red Lion PAXCDC), already inmeter_classes.js. Created itself and started reporting with no code change - Electric meters — reported class
8955, which was unmapped. Two meter rows appeared with nothing indatauntil the class was added. Its register layout turned out to be identical to class48(Veris E51C2), verified column-by-column against the BMO exports- One register was left deliberately unmapped: it reported a stuck value while the other two phases read single digits, meaning it was misconfigured on the device profile
- The meters they replaced were moved into a
Dixon Electricity (Retired)meter group rather than deleted