Open arexms opened 1 year ago
Hi @arexms ,
This question comes up quite a bit. The app uses the Tuya Cloud to compute the energy usage over time (kWh). The Tuya devices themselves do not seem to compute or store this data, though some do have short term memory to send a "pulse" once they reach some threshold (e.g. 0.1kWh). @make-all does a good job explaining it here: https://github.com/jasonacox/tinytuya/issues/274#issuecomment-1413910082
The DPS values from your device look like they track to this: https://github.com/jasonacox/tinytuya#version-33---plug-switch-power-strip-type The one that you are wanting is DPS 17 (Add Electricity) which doesn't show up until it reaches a threshold as mentioned.
The typical approach is to compute energy (W * t) by sampling the wattage over time and compute the area under the cover (see Riemann sum for approximation using samples).
@arexms What happens if you run:
d = tinytuya.OutletDevice( ... )
d.set_socketPersistent( True )
print( d.status() )
d.add_dps_to_request(17)
print( d.status() )
d.updatedps([17,18,19,20])
print( d.status() )
I picked up an energy monitoring plug to see if I could coax this information out of it, and running the above caused mine to start returning DPS 17 on .status() requests. It does also have a few additional DPs though, so it may have a different firmware version.
{'dps': {'1': False, '9': 0, '17': 44, '18': 0, '19': 0, '20': 1196, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
What energy monitoring plug did you get @uzlonewolf ? I want to pick one up. The 2 different types I have didn't respond to this, sadly, but then again they only emit when DPS(17) reaches 1.
FYI - a tiny test loop for fun... if only we had a way of categorizing all our Tuya devices and saying "loop through all devices where type=energy-plugs" 😁
import tinytuya
plugs = []
plugs.append({'name': 'Type 1','id': 'ebabcdefg1234567890aaa', 'ip': '10.0.1.21', 'version': 3.3})
plugs.append({'name': 'Type 2','id': 'ebabcdefg1234567890bbb', 'ip': '10.0.1.22', 'version': 3.3})
plugs.append({'name': 'Type 3','id': 'ebabcdefg1234567890ccc', 'ip': '10.0.1.23', 'version': 3.4})
for plug in plugs:
print("\nLooking at %s [%s @ %s : %s: " % (plug['name'], plug['id'],plug['ip'],plug['version']))
d = tinytuya.OutletDevice(plug['id'],plug['ip'],version=plug['version'])
d.set_socketPersistent( True )
print( d.status() )
d.add_dps_to_request(17)
print( d.status() )
d.updatedps([17,18,19,20])
print( d.status() )
d.close()
print("\nDone")
{'dps': {'1': False, '9': 0, '18': 0, '19': 0, '20': 1209, '21': 1, '22': 1123, '23': 6986, '24': 7260, '25': 4960, '26': 0}}
...
{'dps': {'1': False, '9': 0, '18': 0, '19': 0, '20': 1204, '21': 1, '22': 1136, '23': 7223, '24': 7446, '25': 4830, '26': 0}}
...
{'dps': {'1': False, '9': 0, '38': 'off', '42': '', '43': '', '44': '', '47': 'flip'}}
On further investigation it seems it was not related to add_dps or updatedps at all. I'm still trying to figure out exactly how it works, but it seems to update automatically but only after a certain amount of time has passed. If I just loop and .status() it every 5 minutes it will eventually report power used:
import time
import tinytuya
d = tinytuya.OutletDevice( ... )
print( d.status() )
print(" > Begin Monitor Loop <")
pingtime = time.time() + 9
polltime = time.time() + 300
while(True):
if( pingtime <= time.time() ):
payload = d.generate_payload(tinytuya.HEART_BEAT)
data = d.send(payload)
pingtime = time.time() + 9
if data:
print( data )
if( polltime <= time.time() ):
polltime = time.time() + 300
print( '================' )
print( d.status() )
data = d.receive()
if data:
print( data )
================
{'dps': {'1': False, '9': 0, '17': 24, '18': 0, '19': 0, '20': 1196, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': False, '9': 0, '17': 24, '18': 0, '19': 0, '20': 1196, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
<repeat the '17':24 all night long, 8+ hours>
{'dps': {'1': False, '9': 0, '17': 24, '18': 0, '19': 0, '20': 1196, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': False, '9': 0, '17': 24, '18': 0, '19': 0, '20': 1196, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
{'dps': {'1': True}, 't': 1676739547} <- turned on via app. next update has it reset to "1"
{'dps': {'20': 1200, '18': 778, '19': 935}, 't': 1676739579}
================
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
<restarted script, timer reset>
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
> Begin Monitor Loop <
================
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
The bars are the 5-min timer triggering. At this point I suspect a fixed 30- or 60-minute update period. It also resets back to "1" on certain conditions that I haven't quite figured out yet, but "turn on" seems to be one of them.
I got the "TOPGREENER TGWF115PQM 4-Pack" off Amazon https://www.amazon.com/dp/B07D8ZNNPZ
if only we had a way of categorizing all our Tuya devices and saying "loop through all devices where type=energy-plugs"
Would have been nice if they gave energy-monitoring plugs their own category code, but noooooooo, they re-used the (non-monitoring) smart plug category :(
It just ticked over, and it does appear to be a 30-minute update period. It also seems to reset every period as well; I have a pretty consistent 90w load connected and 45 Wh matches a 30-minute run-time.
> Begin Monitor Loop <
================
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 1, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 44, '18': 778, '19': 935, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
{'dps': {'18': 744, '19': 890}, 't': 1676743182}
================
{'dps': {'1': True, '9': 0, '17': 45, '18': 744, '19': 890, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
================
{'dps': {'1': True, '9': 0, '17': 45, '18': 744, '19': 890, '20': 1200, '21': 1, '22': 1020, '23': 13000, '24': 12506, '25': 2880, '26': 0, '38': 'off', '39': False, '40': 'relay', '41': False, '42': '', '43': '', '44': ''}}
It still needs to be verified, but at this point I feel the operation works like this:
When the switch is Off, DPS 17 holds the last value. When first switched On, if it was off for at least 30 minutes then reset DPS 17 to "1" While the switch is On, update DPS 17 every 30 minutes and reset the internal counter to "1"
thanks @uzlonewolf for your observations. I'll try to do the same, but somehow I've lost a possibility to connect to my devices via local net... I'm receiving "Pooling 192.168... Failed:" during scanning...
But what's interesting below code works fine (via cloud):
c = tinytuya.Cloud()
id = "..."
result = c.getstatus(id)
print("Status of device:\n", json.dumps(result, indent = 4))
And finally I'm getting 'add_ele' value so progress :)
Status of device:
{
"result": [
{
"code": "switch_1",
"value": true
},
{
"code": "countdown_1",
"value": 0
},
{
"code": "add_ele",
"value": 27
},...
@arexms On the local access, make sure you don't have other things connecting to the device at the same time. Many seem to be limited on the number of local connections they can support. I have devices that won't respond locally when I have the Smart Life app open.
o in other words i need to set MyTodayEnergy to 0 at midnight and then check in a loop if dps17 is change and then add new value of dpa17 to MyTodayEnergy (except case that dps17 is change to 1)
I would suggest to just check DPS 17 every 30 minutes and add the value to todays_energy only if DPS 17 is > 1. Isn't it as simple as that?
Isn't it as simple as that?
No, unfortunately it's not. If the switch is Off then DPS 17 will hold the last value even though no power is being used. Another issue is the fact that the 30-minute timer gets reset when the switch gets turned On; if you sample too close to the edge it'll be 50/50 whether you get the old value or the new value.
You are right, I forgot that the last value remains unchanged.
What about combining add_ele with cur_power? E.g. use add_ele only when cur_power was > 0 in the last 30 minutes. I just check that with my data if that assumption makes sense.
i have tuya device without switch only measurement.
€ 27,69 53%OFF | Tuya Smart WiFi Electricity KWH Meter Din Rail Single Phase 110V 230V with 50A 63A CT AC Meter App Real Time Monitor Power
by observating i see it change DPS17 every 10 mins
but anyhow, if device is off (device with switch) is and keep value without change then no problem because I adding DPS17 only in case when it changes value
Just did another test, now with tuya plug with metering. When it is OFF (no load), DPS17 is decreasing value. so in my case it is not keep last value
screenshots..
as see at 18:51 DPS17 from 100 to 71 while plug switched off (DPS1)
That's because within the last measuring interval the switch was not on all the time. It will remain at 71 now till you switch it on again.
as see at 18:51 DPS17 from 100 to 71 while plug switched off (DPS1)
That's because the 30-minute period started at 18:21, and it was turned on from 18:21 to 18:29.
Basically the way it works is like this: When the device is first switched On it starts a 30-minute timer. At the end of those 30 minutes it copies the internal "power used" counter into DPS 17 and resets that internal counter to "1". If the switch is still On it starts the timer again, otherwise it stops and waits until the switch is turned On.
Hello everyone, This is my first attempt to get some basic data from device via Tuya API.
I'm able to connect to device and get status data including dps status. But there is one thing I can't figure out.
Shortly: how to get consumed power from the device?
On the mobile app I can see total (KWh), daily, monthly... but I didn't find such parameter over API. Can you help? My product Type: Socket
EDIT: DPS status output: Device status: {'dps': {'1': True, '9': 0, '18': 321, '19': 586, '20': 2318, '21': 1, '22': 569, '23': 27432, '24': 15037, '25': 2780, '26': 0, '38': 'memory', '39': False, '42': '', '43': ''}}