Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] How to get consumed power from socket #282

Open
arexms opened this issue Feb 11, 2023 · 17 comments
Open

[Question] How to get consumed power from socket #282

arexms opened this issue Feb 11, 2023 · 17 comments
Labels
tuya_device Support for specific Tuya Devices

Comments

@arexms
Copy link

arexms commented Feb 11, 2023

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

image

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': ''}}

@jasonacox jasonacox added the tuya_device Support for specific Tuya Devices label Feb 11, 2023
@jasonacox
Copy link
Owner

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: #274 (comment)

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).

@uzlonewolf
Copy link
Collaborator

@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': ''}}

@jasonacox
Copy link
Owner

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'}}

@uzlonewolf
Copy link
Collaborator

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 :(

@uzlonewolf
Copy link
Collaborator

uzlonewolf commented Feb 18, 2023

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': ''}}

@uzlonewolf
Copy link
Collaborator

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"

@arexms
Copy link
Author

arexms commented Feb 18, 2023

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
        },...

@jasonacox
Copy link
Owner

@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.

@peterodro
Copy link

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)

@fhempy
Copy link
Contributor

fhempy commented Feb 21, 2023

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?

@uzlonewolf
Copy link
Collaborator

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.

@fhempy
Copy link
Contributor

fhempy commented Feb 21, 2023

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.

@peterodro
Copy link

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

@peterodro
Copy link

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

@peterodro
Copy link

peterodro commented Feb 22, 2023

Image 2
screenshots..
as see at 18:51 DPS17 from 100 to 71 while plug switched off (DPS1)

@fhempy
Copy link
Contributor

fhempy commented Feb 22, 2023

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.

@uzlonewolf
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tuya_device Support for specific Tuya Devices
Projects
None yet
Development

No branches or pull requests

5 participants