"sensus-analytics.com" has a json endpoint for utility meters, can we turn it into an integration?

I’ve got a web dashboard that lets me see my utility usage for both my water & electricity usage. I’ve noticed the website uses json endpoints that actually populate this data into the dashboards and I’d love to get it pulled into my Home assistant. Is there anyone who could help me with this?

I’ve gotten as far as tracking down the endpoint in the “Network” tab of my and getting it working in in postman if I copy the request from there, but I can’t figure out anything past that, and as soon as my login-cookie expires on the site, it stops working.

My local power & water company uses a platform called “Sensus Analytics” and you can find ~70 different utility companies using this same analytics platform using this platform with a google search inurl:"sensus-analytics.com" so I think adding support for this one would be really useful for a lot of people.

These json endpoints seems to have everything we need. Here are some samples of the values I was able to get from the JSON endpoint for reference. And I’ve got a bunch of endpoints for my water as well.

{
   "messageList":[
      
   ],
   "operationSuccess":true,
   "widgetList":[
      {
         "id":"meters",
         "premium":false,
         "size":0,
         "dataIdentifier":null,
         "summary":null,
         "data":{
            "labelMaxLength":25,
            "devices":[
               {
                  "usageUnit":"KWH",
                  "meterAddress1":"[censored]",
                  "lastRead":1641700800000,
                  "billing":true,
                  "dailyUsage":22.61,
                  "showToday":true,
                  "meterLong":[censored],
                  "alertCount":15,
                  "meterId":"[censored]",
                  "daily":true,
                  "dailyAlert":35.0,
                  "vacation":true,
                  "billingAlert":1000.0,
                  "multiday":false,
                  "meterLat":[censored],
                  "latestReadUsage":"59468.73",
                  "latestReadTime":1641625200000,
                  "billingUsage":692.7
               }
            ],
            "accountNumber":"[censored]"
         },
         "commodity":"electric"
      }
   ],
   "errors":[
      
   ],
   "messageInfoList":[
      
   ],
   "messageIterator":[
      
   ]
}
{
   "messageList":[
      
   ],
   "operationSuccess":true,
   "widgetList":[
      {
         "id":"usagedetail",
         "premium":false,
         "size":1,
         "dataIdentifier":null,
         "summary":null,
         "data":{
            "ticks":[
               1641099600000,
               1641186000000,
               1641272400000,
               1641358800000,
               1641445200000,
               1641531600000,
               1641618000000
            ],
            "hasPrev":true,
            "usage":[
               [
                  "SKWH",
                  "FAHRENHEIT",
                  "SKWH"
               ],
               [
                  1641099600000,
                  26.067000000002736,
                  37.5,
                  null,
                  null
               ],
               [
                  1641186000000,
                  27.11699999999837,
                  27.5,
                  null,
                  null
               ],
               [
                  1641272400000,
                  21.5679999999993,
                  36.5,
                  null,
                  null
               ],
               [
                  1641358800000,
                  34.5,
                  33.7,
                  null,
                  null
               ],
               [
                  1641445200000,
                  24.930999999996857,
                  16.8,
                  null,
                  null
               ],
               [
                  1641531600000,
                  27.588999999999942,
                  15.9,
                  null,
                  null
               ],
               [
                  1641618000000,
                  22.609999999999996,
                  null,
                  null,
                  null
               ]
            ],
            "tempUnit":"FAHRENHEIT",
            "thresholdAmt":35.0,
            "start":1641099600000,
            "hasNext":false,
            "deviceId":"[censored]",
            "earliest":1582761600000,
            "thresholdUnit":"KWH",
            "showPartDay":false,
            "showToday":true,
            "end":1641704399772,
            "latest":1641704399999
         },
         "commodity":"electric"
      },
      {
         "id":"usagedetail",
         "premium":false,
         "size":0,
         "dataIdentifier":null,
         "summary":null,
         "data":{
            
         },
         "commodity":"electric"
      }
   ],
   "errors":[
      
   ],
   "messageInfoList":[
      
   ],
   "messageIterator":[
      
   ]
}
{
   "operationSuccess":true,
   "errors":[
      
   ],
   "data":{
      "showToday":true,
      "ticks":[
         1612155600000,
         1614574800000,
         1617249600000,
         1619841600000,
         1622520000000,
         1625112000000,
         1627790400000,
         1630468800000,
         1633060800000,
         1635739200000,
         1638334800000,
         1641013200000
      ],
      "hasPrev":true,
      "usage":[
         [
            "SKWH",
            "FAHRENHEIT",
            "SKWH"
         ],
         [
            1612155600000,
            753.086000000003,
            59.7,
            null,
            null
         ],
         [
            1614574800000,
            772.265999999996,
            68.9,
            null,
            null
         ],
         [
            1617249600000,
            827.1870000000054,
            79.4,
            null,
            null
         ],
         [
            1619841600000,
            1082.1349999999948,
            87.3,
            null,
            null
         ],
         [
            1622520000000,
            1612.0080000000016,
            94.3,
            null,
            null
         ],
         [
            1625112000000,
            1594.9660000000003,
            87.0,
            null,
            null
         ],
         [
            1627790400000,
            1785.489999999998,
            93.9,
            null,
            null
         ],
         [
            1630468800000,
            1464.8640000000014,
            91.3,
            null,
            null
         ],
         [
            1633060800000,
            1085.685000000005,
            82.0,
            null,
            null
         ],
         [
            1635739200000,
            913.2959999999948,
            64.4,
            null,
            null
         ],
         [
            1638334800000,
            986.948000000004,
            62.4,
            null,
            null
         ],
         [
            1641013200000,
            215.5509899999955,
            50.9,
            null,
            null
         ]
      ],
      "tempUnit":"FAHRENHEIT",
      "start":1612155600000,
      "zoom":"year",
      "end":1641704399999,
      "hasNext":false,
      "deviceId":"[censored]",
      "earliest":1582761600000,
      "latest":1641704399350
   }
}
{
   "operationSuccess":true,
   "errors":[
      
   ],
   "data":{
      "showToday":true,
      "ticks":[
         1641618000000,
         1641618900000,
         1641619800000,
         1641620700000,
         1641621600000,
         1641622500000,
         1641623400000,
         1641624300000,
         1641625200000,
         1641626100000,
         1641627000000,
         1641627900000,
         1641628800000,
         1641629700000,
         1641630600000,
         1641631500000,
         1641632400000,
         1641633300000,
         1641634200000,
         1641635100000,
         1641636000000,
         1641636900000,
         1641637800000,
         1641638700000,
         1641639600000,
         1641640500000,
         1641641400000,
         1641642300000,
         1641643200000,
         1641644100000,
         1641645000000,
         1641645900000,
         1641646800000,
         1641647700000,
         1641648600000,
         1641649500000,
         1641650400000,
         1641651300000,
         1641652200000,
         1641653100000,
         1641654000000,
         1641654900000,
         1641655800000,
         1641656700000,
         1641657600000,
         1641658500000,
         1641659400000,
         1641660300000,
         1641661200000,
         1641662100000,
         1641663000000,
         1641663900000,
         1641664800000,
         1641665700000,
         1641666600000,
         1641667500000,
         1641668400000,
         1641669300000,
         1641670200000,
         1641671100000,
         1641672000000,
         1641672900000,
         1641673800000,
         1641674700000,
         1641675600000,
         1641676500000,
         1641677400000,
         1641678300000,
         1641679200000,
         1641680100000,
         1641681000000,
         1641681900000,
         1641682800000,
         1641683700000,
         1641684600000,
         1641685500000,
         1641686400000,
         1641687300000,
         1641688200000,
         1641689100000,
         1641690000000,
         1641690900000,
         1641691800000,
         1641692700000,
         1641693600000,
         1641694500000,
         1641695400000,
         1641696300000,
         1641697200000,
         1641698100000,
         1641699000000,
         1641699900000,
         1641700800000,
         1641701700000,
         1641702600000,
         1641703500000
      ],
      "hasPrev":true,
      "usage":[
         [
            "KWH",
            "FAHRENHEIT",
            "KWH"
         ],
         [
            1641618000000,
            0.245,
            null,
            null,
            null
         ],
         [
            1641618900000,
            0.2,
            null,
            null,
            null
         ],
         [
            1641619800000,
            0.204,
            null,
            null,
            null
         ],
         [
            1641620700000,
            0.589,
            null,
            null,
            null
         ],
         [
            1641621600000,
            0.196,
            null,
            null,
            null
         ],
         [
            1641622500000,
            0.213,
            null,
            null,
            null
         ],
         [
            1641623400000,
            0.249,
            null,
            null,
            null
         ],
         [
            1641624300000,
            0.156,
            null,
            null,
            null
         ],
         [
            1641625200000,
            0.215,
            null,
            null,
            null
         ],
         [
            1641626100000,
            0.194,
            null,
            null,
            null
         ],
         [
            1641627000000,
            0.102,
            null,
            null,
            null
         ],
         [
            1641627900000,
            0.195,
            null,
            null,
            null
         ],
         [
            1641628800000,
            0.13,
            null,
            null,
            null
         ],
         [
            1641629700000,
            0.145,
            null,
            null,
            null
         ],
         [
            1641630600000,
            0.196,
            null,
            null,
            null
         ],
         [
            1641631500000,
            0.096,
            null,
            null,
            null
         ],
         [
            1641632400000,
            0.173,
            null,
            null,
            null
         ],
         [
            1641633300000,
            0.177,
            null,
            null,
            null
         ],
         [
            1641634200000,
            0.105,
            null,
            null,
            null
         ],
         [
            1641635100000,
            0.173,
            null,
            null,
            null
         ],
         [
            1641636000000,
            0.226,
            null,
            null,
            null
         ],
         [
            1641636900000,
            0.195,
            null,
            null,
            null
         ],
         [
            1641637800000,
            0.173,
            null,
            null,
            null
         ],
         [
            1641638700000,
            0.183,
            null,
            null,
            null
         ],
         [
            1641639600000,
            0.484,
            null,
            null,
            null
         ],
         [
            1641640500000,
            0.214,
            null,
            null,
            null
         ],
         [
            1641641400000,
            0.126,
            null,
            null,
            null
         ],
         [
            1641642300000,
            0.12,
            null,
            null,
            null
         ],
         [
            1641643200000,
            0.28,
            null,
            null,
            null
         ],
         [
            1641644100000,
            0.177,
            null,
            null,
            null
         ],
         [
            1641645000000,
            0.194,
            null,
            null,
            null
         ],
         [
            1641645900000,
            0.153,
            null,
            null,
            null
         ],
         [
            1641646800000,
            0.195,
            null,
            null,
            null
         ],
         [
            1641647700000,
            0.168,
            null,
            null,
            null
         ],
         [
            1641648600000,
            0.175,
            null,
            null,
            null
         ],
         [
            1641649500000,
            0.28,
            null,
            null,
            null
         ],
         [
            1641650400000,
            0.467,
            null,
            null,
            null
         ],
         [
            1641651300000,
            0.222,
            null,
            null,
            null
         ],
         [
            1641652200000,
            0.474,
            null,
            null,
            null
         ],
         [
            1641653100000,
            0.334,
            null,
            null,
            null
         ],
         [
            1641654000000,
            0.22,
            null,
            null,
            null
         ],
         [
            1641654900000,
            0.211,
            null,
            null,
            null
         ],
         [
            1641655800000,
            0.19,
            null,
            null,
            null
         ],
         [
            1641656700000,
            0.852,
            null,
            null,
            null
         ],
         [
            1641657600000,
            1.241,
            null,
            null,
            null
         ],
         [
            1641658500000,
            0.969,
            null,
            null,
            null
         ],
         [
            1641659400000,
            1.25,
            null,
            null,
            null
         ],
         [
            1641660300000,
            1.206,
            null,
            null,
            null
         ],
         [
            1641661200000,
            1.293,
            null,
            null,
            null
         ],
         [
            1641662100000,
            0.971,
            null,
            null,
            null
         ],
         [
            1641663000000,
            0.149,
            null,
            null,
            null
         ],
         [
            1641663900000,
            0.144,
            null,
            null,
            null
         ],
         [
            1641664800000,
            0.225,
            null,
            null,
            null
         ],
         [
            1641665700000,
            0.508,
            null,
            null,
            null
         ],
         [
            1641666600000,
            0.113,
            null,
            null,
            null
         ],
         [
            1641667500000,
            0.138,
            null,
            null,
            null
         ],
         [
            1641668400000,
            0.145,
            null,
            null,
            null
         ],
         [
            1641669300000,
            0.09,
            null,
            null,
            null
         ],
         [
            1641670200000,
            0.153,
            null,
            null,
            null
         ],
         [
            1641671100000,
            0.137,
            null,
            null,
            null
         ],
         [
            1641672000000,
            0.112,
            null,
            null,
            null
         ],
         [
            1641672900000,
            0.137,
            null,
            null,
            null
         ],
         [
            1641673800000,
            0.11,
            null,
            null,
            null
         ],
         [
            1641674700000,
            0.14,
            null,
            null,
            null
         ],
         [
            1641675600000,
            0.145,
            null,
            null,
            null
         ],
         [
            1641676500000,
            0.151,
            null,
            null,
            null
         ],
         [
            1641677400000,
            0.178,
            null,
            null,
            null
         ],
         [
            1641678300000,
            0.137,
            null,
            null,
            null
         ],
         [
            1641679200000,
            0.672,
            null,
            null,
            null
         ],
         [
            1641680100000,
            0.13,
            null,
            null,
            null
         ],
         [
            1641681000000,
            0.258,
            null,
            null,
            null
         ],
         [
            1641681900000,
            0.15,
            null,
            null,
            null
         ],
         [
            1641682800000,
            0.153,
            null,
            null,
            null
         ],
         [
            1641683700000,
            0.225,
            null,
            null,
            null
         ],
         [
            1641684600000,
            0.137,
            null,
            null,
            null
         ],
         [
            1641685500000,
            0.126,
            null,
            null,
            null
         ],
         [
            1641686400000,
            0.1,
            null,
            null,
            null
         ],
         [
            1641687300000,
            0.199,
            null,
            null,
            null
         ],
         [
            1641688200000,
            0.081,
            null,
            null,
            null
         ],
         [
            1641689100000,
            0.183,
            null,
            null,
            null
         ],
         [
            1641690000000,
            0.088,
            null,
            null,
            null
         ],
         [
            1641690900000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641691800000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641692700000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641693600000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641694500000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641695400000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641696300000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641697200000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641698100000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641699000000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641699900000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641700800000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641701700000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641702600000,
            0.0,
            null,
            null,
            null
         ],
         [
            1641703500000,
            0.0,
            null,
            null,
            null
         ]
      ],
      "tempUnit":"FAHRENHEIT",
      "start":1641618000000,
      "zoom":"day",
      "end":1641704399999,
      "hasNext":false,
      "deviceId":"[censored]",
      "earliest":1582761600000,
      "latest":1641704399989
   }
}

If someone is interested in making this integration, I’d be happy to provide access to my analytics account for testing.

I’ve never made an integration before but it’s on my list of things to do. I’m emailing my current utility provider and Sensus to see if API access is available. I’ll report back on what they say.

1 Like

this looks very interesting
was told by my city that hour water meters report usage every hour
they were nice enought to provide a printout of usage and across bottom I found
https://CTYST.sensus-analytics.com/deviceaccess#?/returnable=true&devicenuber=########

your google link shows indeed many cities are using sensus-analytics.com services

Did your city provide access credentials for you ?

Have you found more success in your efforts here ?

thx

1 Like

Wondering if anyone has looked into this further. Very much interested now that water usage has been added to the energy dashboard and my utility company uses this portal.

Thanks!

1 Like

Check in after a year, unfortunately there’s still no movement here. With my very minimal level understanding I was able to reproduce these JSON endpoints reliably in Postman to give me my current AND historical usage data in a JSON format, but I know nothing about authentication so as soon as my login expires I’ve got to manually re-log in, and re-copy the authentication headers from my browser’s dev console.

If someone actually knew what they were doing here it seems like this would be a slam dunk integration that would get lots more users being able to integrate their Utilities into their Energy Dashboard. I know I can access both my water and electric meters through this website, and I’d be happy to help make this happen however I can.

And just to again show, there are even more utilities using this platform than before:
https://www.google.com/search?q=inurl:sensus-analytics.com&filter=0

Yeah, our city did provide login access for customers. Looks like the login page isn’t working for yours tho. Our login page is at “/login.html” but when I try to go to that on your url there, it just gives an error page. I’d reach out to your utility and see if you can convince them to give you user access to your dashboard. This is what it looks like for us. Assming they’re using the same playform this should be available to you :man_shrugging:


u

I’m also very interested in trying to make this work. I did a bit of poking around but couldn’t figure out the authentication since it seems to be quite complex and a lot of transactions occur.
This is the output from my City’s portal in Coppell Texas.

1 Like

Just make sure you’re voting for it at the top of the thread, unfortunately it looks like there’s not enough interest yet to attract anyone to do anything with it.

I would love to see someone develop a connector for sensus-analytics (.com/.ca). As more cities start to transition to smart meters I expect the potential user base for this will grow

1 Like

Just wondering if anyone has been able to extract the daily water usage data out of this site?

I was able to get it half way working in node red. Lost my setup going to work on it again. Just a clue for authentication, the use an old Spring Authentication feature.

I was able to write the following BASH script to extract data from my local town’s utility. Not sure if it is helpful or not or if someone would want to add on. Basically I used developer tools in Chrome and network captured each step from logging in to capturing the daily and hourly data.

#! /bin/bash

# Ask for user input for start date/time
read -p "Enter start date/time (mm/dd/yy hh:mm): " start_datetime

# Ask for user input for end date/time
read -p "Enter end date/time (mm/dd/yy hh:mm): " end_datetime

# Function to convert date/time to Unix timestamp
orig_start=$start_datetime
epoch_start=`expr $(date -d "${orig_start}" +"%s") \* 1000`
epoch_to_date_start=$(date -d @$epoch_start +%Y%m%d_%H%M%S)    

orig_end=$end_datetime
epoch_end=`expr $(date -d "${orig_end}" +"%s") \* 1000`
epoch_to_date_end=$(date -d @$epoch_end +%Y%m%d_%H%M%S)    


curl 'https://my-town.sensus-analytics.com/j_spring_security_check' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'cache-control: max-age=0' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -H 'cookie: defaultUnits=rain%3DINCHES%7Ctemp%3DF%7Celectric%3DKWH%7Cgas%3DCCF%7Cwater%3DGAL%7C; widgetOrderWATER=Alerts%2CBill%2CGoal%2CWater%2CMActivity%2CNotifications%2CThreshold%2CUsage%2CBudget%2COutdoors%2COUTDOORS%2CPEERS%2CTERMS; widgetOrderGAS=Alerts%2CBill%2CGoal%2CMActivity%2CNotifications%2CThreshold%2CUsage%2COUTDOORS%2CTERMS; widgetOrderELECTRIC=Alerts%2CBill%2CGoal%2CMActivity%2CNotifications%2CThreshold%2CUsage%2COUTDOORS%2CTERMS; disclaimers=false; theme=white; JSESSIONID=074EC68AB845DD09D1E067679EFDCB81; AWSALB=PtQkySWIrRXGSdIb1DjZXkcoT7E1sN1h2JTEcnGzJ8pagm8SzO/p2xIqYVL6VghbMeQllJNOsMC/Ud317sQO7XbCps8mN9ko2Ppbu+x81GhoaPsmxefwNCH4tocC; AWSALBCORS=PtQkySWIrRXGSdIb1DjZXkcoT7E1sN1h2JTEcnGzJ8pagm8SzO/p2xIqYVL6VghbMeQllJNOsMC/Ud317sQO7XbCps8mN9ko2Ppbu+x81GhoaPsmxefwNCH4tocC' \
  -H 'dnt: 1' \
  -H 'origin: https://my-town.sensus-analytics.com' \
  -H 'priority: u=0, i' \
  -H 'referer: https://my-town.sensus-analytics.com/login.html' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: document' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-site: same-origin' \
  -H 'sec-fetch-user: ?1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  -c cookies.txt \
  --data-raw 'j_username=myemailaddress%40gmail.com&j_password=plaintextpassword' \
  --silent --output /dev/null ;
curl 'https://my-town.sensus-analytics.com/main.html' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
  -H 'accept-language: en-US,en;q=0.9' \
  -H 'cache-control: max-age=0' \
  -b cookies.txt \
  -c cookies2.txt \
  -H 'dnt: 1' \
  -H 'priority: u=0, i' \
  -H 'referer: https://my-town.sensus-analytics.com/login.html' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: document' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-site: same-origin' \
  -H 'sec-fetch-user: ?1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  --silent --output /dev/null ;
curl 'https://my-town.sensus-analytics.com/main.html' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
  -H 'accept-language: en-US,en;q=0.9' \
  -b cookies2.txt \
  -c cookies.txt \
  -H 'dnt: 1' \
  -H 'priority: u=0, i' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: document' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-site: none' \
  -H 'sec-fetch-user: ?1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  --silent --output /dev/null ;
curl 'https://my-town.sensus-analytics.com/login.html' \
  -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
  -H 'accept-language: en-US,en;q=0.9' \
  -b cookies.txt \
  -c cookies2.txt \
  -H 'dnt: 1' \
  -H 'priority: u=0, i' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: document' \
  -H 'sec-fetch-mode: navigate' \
  -H 'sec-fetch-site: none' \
  -H 'sec-fetch-user: ?1' \
  -H 'upgrade-insecure-requests: 1' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  --silent --output /dev/null ;
curl 'https://my-town.sensus-analytics.com/styles/bootstrap.min.css?v=3.5.12.202404221740' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'Referer: https://my-town.sensus-analytics.com/login.html' \
  -H 'DNT: 1' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  -H 'sec-ch-ua-platform: "Linux"' \
  --silent --output /dev/null ;
curl 'https://my-town.sensus-analytics.com/styles/login.css?v=3.5.12.202404221740' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'Referer: https://my-town.sensus-analytics.com/login.html' \
  -H 'DNT: 1' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  -H 'sec-ch-ua-platform: "Linux"' \
  --silent --output /dev/null ;
curl 'https://my-town.sensus-analytics.com/brand/public/theme?v=3.5.12.202404221740' \
  -H 'accept: text/css,*/*;q=0.1' \
  -H 'accept-language: en-US,en;q=0.9' \
  -b cookies2.txt \
  -c cookies.txt \
  -H 'dnt: 1' \
  -H 'priority: u=0' \
  -H 'referer: https://my-town.sensus-analytics.com/login.html' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: style' \
  -H 'sec-fetch-mode: no-cors' \
  -H 'sec-fetch-site: same-origin' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  --silent --output /dev/null ;

eval " 
 curl 'https://my-town.sensus-analytics.com/water/usage/019146-003/78543712?start="${epoch_start}"&end="${epoch_end}"&zoom=custom&page=null&weather=1' \
  -H 'accept: application/json, text/javascript, */*; q=0.01' \
  -H 'accept-language: en-US,en;q=0.9' \
  -b cookies.txt \
  -c cookies2.txt \
  -H 'dnt: 1' \
  -H 'priority: u=1, i' \
  -H 'referer: https://my-town.sensus-analytics.com/main.html' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: same-origin' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  -H 'x-requested-with: XMLHttpRequest' \
  -o waterdata_daily.txt"

eval "  
 curl 'https://my-town.sensus-analytics.com/water/usage/019146-003/78543712?start="${epoch_start}"&end="${epoch_end}"&zoom=day&page=null&weather=1' \
  -H 'accept: application/json, text/javascript, */*; q=0.01' \
  -H 'accept-language: en-US,en;q=0.9' \
  -b cookies2.txt \
  -c cookies.txt \
  -H 'dnt: 1' \
  -H 'priority: u=1, i' \
  -H 'referer: https://my-town.sensus-analytics.com/main.html' \
  -H 'sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Linux"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: same-origin' \
  -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36' \
  -H 'x-requested-with: XMLHttpRequest' \
  -o waterdata_hourly.txt"

Hi- any chance you’ve made this into an integration?

Unfortunately no, but it is not a personally high priority item. The output text files are json format as the OP discussed, and I am not familiar enough with HA and what method would be best for importing it. I am open to ideas if anyone has done something similar before. I am not sure how to do the authentication outside of my curl method.

I recently had my meter upgraded to use the same service so was poking around on how to integrate it into HA.

I did some reverse-engineering of their APIs and I was able to successfully poll data from my account using the following python script:

#!/usr/bin/python3

import datetime
import requests
import time

base_url = 'https://<login_subdomain>.sensus-analytics.com/'
username = '<login_email>'
password = '<login_password>'

# login
r_sec = requests.post(f'{base_url}/j_spring_security_check', data={'j_username':username, 'j_password':password}, allow_redirects=False)

# query account data
r_acct = requests.post(f'{base_url}/init/init', json={}, cookies=r_sec.cookies)
for account in r_acct.json()['userSettings']['accounts']:

    acct_num = account['accountId']
    print(f'Account Number: {acct_num}')
    print();

    for commodity in account['commodities']:
        # query meter data
        r_meter = requests.post(f'{base_url}/account/details', json={'accountNumber':acct_num, 'meterTypeByValue':commodity}, cookies=r_sec.cookies)

        for meter_num in r_meter.json()['deviceIdList']:

            print(f'{commodity.capitalize()} Meter: {meter_num} ({r_meter.json()["devices"][meter_num]["state"]})')

            # query meter data
            r_data = requests.get(f'{base_url}/{commodity}/usage/dailyavg/{acct_num}/{meter_num}', cookies=r_sec.cookies)
            print(f'Daily avg: {r_data.json().get("data").get("dailyAvg", {}).get("usage", "-")} {r_data.json().get("data").get("dailyAvg", {}).get("usageUnit", "-")}')
            r_data = requests.get(f'{base_url}/{commodity}/usage/multidayavg/{acct_num}/{meter_num}', cookies=r_sec.cookies)
            print(f'Weekly avg: {r_data.json().get("data").get("multidayAvg", {}).get("usage", "-")} {r_data.json().get("data").get("multidayAvg", {}).get("usageUnit", "-")}')
            r_data = requests.get(f'{base_url}/{commodity}/usage/billingavg/{acct_num}/{meter_num}', cookies=r_sec.cookies)
            print(f'Billing avg: {r_data.json().get("data").get("billingAvg", {}).get("usage", "-")} {r_data.json().get("data").get("billingAvg", {}).get("usageUnit", "-")}')

            # query usage data
            r_data = requests.get(f'{base_url}/{commodity}/usage/{acct_num}/{meter_num}?start={int(time.mktime(datetime.date.today().timetuple())*1000)}&end={int(time.mktime(datetime.date.today().timetuple())*1000)}&zoom=custom&page=null&weather=false', cookies=r_sec.cookies)
            print('Usage data for today:')
            usageUnit = r_data.json()['data']['usage'][0][0]
            for usage in r_data.json()['data']['usage'][1:]:
                tick = datetime.datetime.fromtimestamp(usage[0] / 1000.0)
                print(f'  {tick.strftime("%Y/%m/%d %H:%M")}: {usage[1]} {usageUnit}')

It looks like the primary auth flow is just to call the security check endpoint to get the cookie returned then pass that back into the usage endpoints. There are a few endpoints that return generic information about the accounts and meters associated with a given user.

My account only has a water meter on it, but based on the APIs it looks like technically there are probably gas and electric endpoints as well. Provided they all follow the same API structure this should work for any of them.

This should be enough data to throw together a custom integration with configuration where you provide the endpoint and auth and it can add various meters as devices. Could query the usage endpoint hourly for updates.

Not sure if/when I’ll get time to actually create an integration but wanted to throw this script out there in case I don’t get time and someone else wants to take a stab at it.

2 Likes

I “simplified” @cvpcs’s script above (thanks!) to return only the current water meter reading. Saved the script as /config/water.py:

#!/usr/bin/python3

import datetime
import requests
import time

base_url = 'https://<your_utility>.sensus-analytics.com/'

username = '<your_userid>'
password = '<your_password>'
acct_num = '<your_account>'

meter_num = '<your_meter>'

# login
r_sec = requests.post(f'{base_url}/j_spring_security_check', data={'j_username':username, 'j_password':password}, allow_redirects=False)
#read meter
r_data = requests.post(f'{base_url}/water/widget/byPage', json={"group":"meters","accountNumber":acct_num,"deviceId":meter_num,"minSize":4,"maxSize":4,"touch":"false","desktop":"true","meterCount":2}, cookies=r_sec.cookies)

print(f'{r_data.json().get("widgetList")[0].get("data").get("devices")[0].get("latestReadUsage")}')

Then created a command line sensor to read this (add to configuration.yaml):

command_line:
  - sensor:
      unique_id: water_<your_meter>
      name: water_meter
      command: "/usr/local/bin/python /config/water.py"
      unit_of_measurement: "gal"
      scan_interval: 3600
      state_class: total_increasing
      device_class: water

My utility only updates the number every 4 hours. So, I run the script hourly (3,600 seconds).
This is not a very “sophisticated” solution, but it’s enough to get the water meter onto my Energy dashboard (I may look at a “proper” integration… eventually)! I’m not sure how consistent the json structures returned by the various utilities are, this one works for Salt Lake City water. You may need to tweak the print() line for your use.

2 Likes

Thanks for this. Got it working for my utility provider in Texas.

I was able to get this to work for my local util that uses that Sensus analytics platform.

One thing to watch for is units. Code above returns the meter reading & labels it “gal”. In my case, I had the Sensus portal configured to display in gallons - but the meter reading turned out to be still in cubic feet. I decided I wanted HomeAssistant to be gallons, so I changed the last line in water.py to

print(f’{float(r_data.json().get(“widgetList”)[0].get(“data”).get(“devices”)[0].get(“latestReadUsage”)) * 7.48}')

(7.48 gallons/cubic foot)
The alternative would have been to change the unit_of_measure to “ft³”.