magico13 / PyEmVue

Python Library for the Emporia Vue Energy Monitor
MIT License
185 stars 36 forks source link

Documentation examples, I need more instructions... #80

Open Merca60 opened 1 month ago

Merca60 commented 1 month ago

Dear magico13, I open a new issue because to comment an old one (#32) don't generate a notice, I think.

Using your example(s) I obtained correct results only with the one you shown results in the "readme.md" file. I'm aware to need learnig on Python, Java and more other things, like a lot of people...

For a programmer like you it's easy and fast to complete coding of your examples, but a lot of us dont'know too much things. During my training I made various trite errors, (syntax and others), after I founded Spyder, it's a great help to have a correct Python syntax.

Then, in the first example of readme.md file we obtain the list of instant data of all devices matched with our Emporia's account. It's correct, I have two devices and I obtain both's data. (Not always, and sometimes not all, I think it depend from the instant conection quality)

During last year I red a lot of news, forum etc on Emporia vue, like the Emporia's staff criticism on the enormous quantity of requests to them servers originated by your software, and it's normal, because if you need to obtain high resolution data you'll ask continuosly to servers...

So I'd like to ask only when I need, for example for a second resolution every less than three hours, because three hours are the limit of seconds interval on Emporia servers. (or three days for the minute, and so on).

It is the readme.md section I thought to use.

### Get usage over time

# ```python
vue = PyEmVue()
vue.login(id_token='id_token',
    access_token='access_token',
    refresh_token='refresh_token')

devices = vue.get_devices()

usage_over_time, start_time = vue.get_chart_usage(devices[0].channels[0], datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(days=7), datetime.datetime.now(datetime.timezone.utc), scale=Scale.DAY.value, unit=Unit.KWH.value)

print('Usage for the last seven days starting', start_time.isoformat())
for usage in usage_over_time:
    print(usage, 'kwh')
# ```

Gets the usage in the scale and unit provided over the given time range. Returns a tuple with the first element the usage list and the second the datetime that the range starts.

#### Arguments

- **channel**: A VueDeviceChannel object, typically pulled from a VueDevice.
- **start**: The start time for the time period. Defaults to now if None.
- **end**: The end time for the time period. Default to now if None.
- **scale**: The time scale to check the usage over.
- **unit**: The unit of measurement.

It is the code I wrote the first time, adding the first two rows from the other code.

import pyemvue 
from pyemvue.enums import Scale, Unit

### Get usage over time

# ```python
vue = pyemvue()
vue.login(id_token='id_token',
    access_token='access_token',
    refresh_token='refresh_token')

devices = vue.get_devices()

usage_over_time, start_time = vue.get_chart_usage(devices[0].channels[0], datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(days=7), datetime.datetime.now(datetime.timezone.utc), scale=Scale.DAY.value, unit=Unit.KWH.value)

print('Usage for the last seven days starting', start_time.isoformat())
for usage in usage_over_time:
    print(usage, 'kwh')
# ```

# Gets the usage in the scale and unit provided over the given time range. Returns a tuple with the first element the usage list and the second the datetime that the range starts.

#### Arguments

# - **channel**: A VueDeviceChannel object, typically pulled from a VueDevice.
# - **start**: The start time for the time period. Defaults to now if None.
# - **end**: The end time for the time period. Default to now if None.
# - **scale**: The time scale to check the usage over.
# - **unit**: The unit of measurement.

Obtaining the next message on the console.

Traceback (most recent call last):
  File "/home/ammi/PyEmVue-master/PyEmVue/PPyEmVue_27.py", line 15, in <module>
    vue = pyemvue()
TypeError: 'module' object is not callable

It is the code I wrote the second time (replacing the "vue =" rows with the other proven for my account access).

import pyemvue 
from pyemvue.enums import Scale, Unit

# Crea l'oggetto "vue" (?)
vue = pyemvue.PyEmVue()
vue.login(username='MIAMAIL', password='MIAPASSWORD', token_storage_file='keys=json')
# 

### Get usage over time

## # ```python
## vue = pyemvue()
## vue.login(id_token='id_token',
##     access_token='access_token',
##     refresh_token='refresh_token')

devices = vue.get_devices()

usage_over_time, start_time = vue.get_chart_usage(devices[0].channels[0], datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(days=7), datetime.datetime.now(datetime.timezone.utc), scale=Scale.DAY.value, unit=Unit.KWH.value)

print('Usage for the last seven days starting', start_time.isoformat())
for usage in usage_over_time:
    print(usage, 'kwh')
# ```

# Gets the usage in the scale and unit provided over the given time range. Returns a tuple with the first element the usage list and the second the datetime that the range starts.

#### Arguments

# - **channel**: A VueDeviceChannel object, typically pulled from a VueDevice.
# - **start**: The start time for the time period. Defaults to now if None.
# - **end**: The end time for the time period. Default to now if None.
# - **scale**: The time scale to check the usage over.
# - **unit**: The unit of measurement.

Obtaining the next message on the console.

Traceback (most recent call last):
  File "/home/ammi/PyEmVue-master/PyEmVue/PPyEmVue_28.py", line 26, in <module>
    usage_over_time, start_time = vue.get_chart_usage(devices[0].channels[0], datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(days=7), datetime.datetime.now(datetime.timezone.utc), scale=Scale.DAY.value, unit=Unit.KWH.value)
NameError: name 'datetime' is not defined

It is the code I wrote the third time (inserting datatime call).

import pyemvue 
from pyemvue.enums import Scale, Unit
from datetime import datetime

# Crea l'oggetto "vue" (?)
vue = pyemvue.PyEmVue()
vue.login(username='MIAMAIL', password='MIAPASSWORD', token_storage_file='keys=json')
# 

### Get usage over time

## # ```python
## vue = pyemvue()
## vue.login(id_token='id_token',
##     access_token='access_token',
##     refresh_token='refresh_token')

devices = vue.get_devices()

usage_over_time, start_time = vue.get_chart_usage(devices[0].channels[0], datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(days=7), datetime.datetime.now(datetime.timezone.utc), scale=Scale.DAY.value, unit=Unit.KWH.value)

print('Usage for the last seven days starting', start_time.isoformat())
for usage in usage_over_time:
    print(usage, 'kwh')
# ```

# Gets the usage in the scale and unit provided over the given time range. Returns a tuple with the first element the usage list and the second the datetime that the range starts.

#### Arguments

# - **channel**: A VueDeviceChannel object, typically pulled from a VueDevice.
# - **start**: The start time for the time period. Defaults to now if None.
# - **end**: The end time for the time period. Default to now if None.
# - **scale**: The time scale to check the usage over.
# - **unit**: The unit of measurement.

Obtaining the next message on the console.

Traceback (most recent call last):
  File "/home/ammi/PyEmVue-master/PyEmVue/PPyEmVue_28.py", line 26, in <module>
    usage_over_time, start_time = vue.get_chart_usage(devices[0].channels[0], datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(days=7), datetime.datetime.now(datetime.timezone.utc), scale=Scale.DAY.value, unit=Unit.KWH.value)
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'

Today, reading all other issues (open and not) I found the #42 code. Tried it and under the result Here the issue #42 code


# Copied from magico13 + pyemvue #42 issue

from pyemvue.pyemvue import PyEmVue, Scale, Unit
import datetime

username='mymail'
password='mypsw'

vue = PyEmVue()
vue.login(username, password)
print('Logged in.')
print()

device_gids = []
devices = vue.get_devices()
for device in devices:
    if not device.device_gid in device_gids: device_gids.append(device.device_gid)

now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=-5))) # This is my timezone, yours may vary
usageDict = vue.get_device_list_usage(device_gids, now, Scale.SECOND.value, Unit.KWH.value)
for (gid, usageDevice) in usageDict.items():
    for (channelNum, usageChannel) in usageDevice.channels.items():
        # unit is kWh/s, multiply by 3600 seconds/hour to get kW, multiply by 1000 to get W
        # if the usage is ever None this will throw an exception so you should check for that and replace it with 0
        scaledUsage = 3600*1000*usageChannel.usage
        print(f'{gid} - {channelNum} - {usageChannel.name} - {scaledUsage}W')

Obtaining the next message on the console.

Traceback (most recent call last):
  File "/home/ammi/PyEmVue-master/PyEmVue/PPyEmVue_28.py", line 26, in <module>
    usage_over_time, start_time = vue.get_chart_usage(devices[0].channels[0], datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(days=7), datetime.datetime.now(datetime.timezone.utc), scale=Scale.DAY.value, unit=Unit.KWH.value)
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'

So I need to ask to you.

I think you could write some code rows more than actual readme file... I hope it.

magico13 commented 1 month ago

Hello, you're primarily running into issues with python imports and its syntax. For datetime in particular it's tricky, because datetime is both a module and also an object within the module, ie datetime.datetime.

If you do from datetime import datetime then whenever you put the word datetime you're referencing the object. If you do import datetime then you're importing the module, and thus whenever you put the word datetime you're only referencing the module, and you have to put datetime.datetime to reference the object.

Your last error refers to code that looks like datetime.datetime.now(datetime.timezone.utc), meaning it is expecting import datetime and not from datetime import datetime. You can also tell because it is using datetime.timezone, where timezone is an object inside the datetime module. If you take your code and just swap from datetime import datetime to import datetime it appears as if it will run just fine (I also swapped the = for a . in the token storage file name)

import pyemvue
from pyemvue.enums import Scale, Unit
import datetime

# Crea l'oggetto "vue" (?)
vue = pyemvue.PyEmVue()
vue.login(username='MIAMAIL', password='MIAPASSWORD', token_storage_file='keys.json')
#

### Get usage over time

## # ```python
## vue = pyemvue()
## vue.login(id_token='id_token',
##     access_token='access_token',
##     refresh_token='refresh_token')

devices = vue.get_devices()

usage_over_time, start_time = vue.get_chart_usage(
    devices[0].channels[0],
    datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=7),
    datetime.datetime.now(datetime.timezone.utc),
    scale=Scale.DAY.value,
    unit=Unit.KWH.value,
)

print("Usage for the last seven days starting", start_time.isoformat())
for usage in usage_over_time:
    print(usage, "kwh")
# ```

# Gets the usage in the scale and unit provided over the given time range. Returns a tuple with the first element the usage list and the second the datetime that the range starts.

#### Arguments

# - **channel**: A VueDeviceChannel object, typically pulled from a VueDevice.
# - **start**: The start time for the time period. Defaults to now if None.
# - **end**: The end time for the time period. Default to now if None.
# - **scale**: The time scale to check the usage over.
# - **unit**: The unit of measurement.

If you want the last hour of one-second data, it might look something like this. You'll have to iterate over all of the devices and/or channels, this is just checking the first channel on the first device. I also got an error if I requested more than 4000 data points, or 1 hour, 6 minutes, and 40 seconds worth of data.

import pyemvue
from pyemvue.enums import Scale, Unit
import datetime

vue = pyemvue.PyEmVue()
# Note, once you've logged in with your email and password once, you can just use the token file to log in.
vue.login(token_storage_file="keys.json")

devices = vue.get_devices()

usage_over_time, start_time = vue.get_chart_usage(
    devices[0].channels[0],
    datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=1),
    datetime.datetime.now(datetime.timezone.utc),
    scale=Scale.SECOND.value,
    unit=Unit.KWH.value,
)

print("Watts each second for the last hour starting at", start_time.isoformat())
for usage in usage_over_time:
    print(usage * 3600 * 1000, "W")
# Note, the usage is always in kilowatt-hour regardless of the scale
# so you must scale it yourself if you want kilowatts, by multiplying by 3600 seconds per 1 hour
# if you want watts, multiply again by 1000 watts per 1 kilowatt
Merca60 commented 1 month ago

Thanks for your support! I followed your advices and at last got something, but...

I have two devices, with 6 and 10 channels. With this code I thought to change the "zero" of "device" and "channels" parenthesis to obtain data, and, if I write "hours = 1" I thought to obtain 3600 rows. But it isn't so, rows are always less than 3600, and not the same number...

If I change the "channels" zero to, for example, 1, 3, 6, I obtain, always, the error "list index out of range". If I change the"device" zero I obtain lists of data with 0,1,2,3 numbers, but, I remember, I have only two devices. Only using "4" obtain "list index out of range" error too.

And, please, I need to print the channel number and name in the firs row to identify what data are them. I haven't an idea to do it...

import pyemvue
from pyemvue.enums import Scale, Unit
import datetime

vue = pyemvue.PyEmVue()
# Note, once you've logged in with your email and password once, you can just use the token file to log in.
vue.login(token_storage_file="keys.json")

devices = vue.get_devices()

usage_over_time, start_time = vue.get_chart_usage(
    devices[0].channels[0],
    datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=1),
    datetime.datetime.now(datetime.timezone.utc),
    scale=Scale.SECOND.value,
    unit=Unit.KWH.value,
)

print("Watts each second for the last hour starting at", start_time.isoformat())
for usage in usage_over_time:
    print(usage * 3600 * 1000, "W")
# Note, the usage is always in kilowatt-hour regardless of the scale
# so you must scale it yourself if you want kilowatts, by multiplying by 3600 seconds per 1 hour
# if you want watts, multiply again by 1000 watts per 1 kilowatt

I tried the underlying code too, and verified this behaviour: Writing 0 between parenthesis I obtain two rows, both with the first device's name. Writing 1 between parenthesis I obtain two rows, the first empty and the second wit the previous device's name. Writing 2 between parenthesis I obtain two rows both with the second device's name. Writing 3 between parenthesis I obtain two rows, the first empty and the second wit the previous device's name. Writing 4 between parenthesis I obtain the error "list index out of range"

Defining "device0 to 4" I can get and print all these. Why a single device answer to two different checks?

How is it possible to extract the channel names too?

import pyemvue 
from pyemvue.enums import Scale, Unit

vue = pyemvue.PyEmVue()
vue.login(token_storage_file="keys.json")

# Get additional device properties    
device1 = vue.get_devices()[0]
print(device1.device_name), # prints ""
device1 = vue.populate_device_properties(device1)
print(device1.device_name) # prints "Home"
Merca60 commented 1 month ago

Mmhh. I'm starting to undertand a little more.

I tried the underlying code I understood this: The "object" vue.get_devices() takes data from the device, and we need to ask what data to get from. Then, I didn't find "names" of these data, except "device.name". Where?

import pyemvue 
from pyemvue.enums import Scale, Unit

vue = pyemvue.PyEmVue()
vue.login(token_storage_file="keys.json")
# 

vue.get_devices()
device2 = vue.get_devices()    
print(device2, 'Zero'),

# Get additional device properties    
device10 = vue.get_devices()[0]
print(device10.device_name, 'Uno'), # prints ""
device10 = vue.populate_device_properties(device10)
print(device10.device_name, 'Uno'), # prints "Home"

device11 = vue.get_devices()[1]
print(device11.device_name, 'Due'), # prints ""
device11 = vue.populate_device_properties(device11)
print(device11.device_name, 'Due'),  # prints "Home"

device12 = vue.get_devices()[2]
print(device12.device_name, 'Tre'),  # prints ""
device12 = vue.populate_device_properties(device12)
print(device12.device_name, 'Tre'),  # prints "Home"

device13 = vue.get_devices()[3]
print(device13.device_name, 'Quattro'),  # prints ""
device1 = vue.populate_device_properties(device13)
print(device13.device_name, 'Quattro'),  # prints "Home"

Results from the code. (I inserted commas to check).

[<pyemvue.device.VueDevice object at 0x7aab6ac63d90>, <pyemvue.device.VueDevice object at 0x7aab68bdfac0>, <pyemvue.device.VueDevice object at 0x7aab6ac63ee0>, <pyemvue.device.VueDevice object at 0x7aab68bdfcd0>] , Zero
Name_A , Uno
Name_A , Uno
 , Due
Name_A , Due
Name_B , Tre
Name_B , Tre
 , Quattro
Name_B , Quattro