Hi guys!
I’m trying to build a small custom component to have my energy usage integrated.
There’s an android app called Eon Smart Control(E.ON Smart Control – Apps bei Google Play) that i use with a separate Lora GW which is connected to my electricity meter.
This is what i have been using in past with additional sensor configuration
#!/usr/bin/python3
# encoding: utf8
#
import requests, json
import os, time
from pathlib import Path
from datetime import datetime
from requests.structures import CaseInsensitiveDict
username = "EMAIL"
password = "PASSWORD"
auth_token_file=".auth-token"
today = "{:%Y-%m-%d}".format(datetime.now())
now = "{:%Y-%m-%dT%H:%M:%S.%f}".format(datetime.now())[:-3]
timeout=5
def file_age(filepath):
seconds = str(time.time() - os.path.getmtime(filepath))
return int(seconds.split('.')[0])
def get_token(username, password):
file_location = Path(auth_token_file)
if not file_location.is_file() or file_age(auth_token_file) >= 3600:
url = "https://smartcontrol.eon.de/auth"
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/json"
data = '{"username":"%s","password":"%s","method":"login"}' % (username, password)
response = requests.get(url, headers=headers, data=data, allow_redirects=False, timeout=timeout)
data=json.loads(response.text)
return(data['access_token'])
else:
token=open(auth_token_file, 'r')
return(token.readline())
def get_watts(access_token):
url = "https://api.n2g-iona.net/v2/power/%sT00:00:00.000Z/%sZ" % (today, now)
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = "Bearer %s" % (access_token)
response = requests.get(url, headers=headers, timeout=timeout)
data=json.loads(response.text)
my_dict={'power':data['data']['results'][-1]['power']}
return(my_dict)
def get_kWh(access_token):
url = "https://api.n2g-iona.net/v2/meter/info"
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = "Bearer %s" % (access_token)
response = requests.get(url, headers=headers, timeout=timeout)
data=json.loads(response.text)
kWh=int(data['data']['Electricity']['CSD'])
rounded = round(kWh / 1000)
my_dict={'kWh': rounded}
return(my_dict)
def main():
access_token=get_token(username, password)
print(get_kWh(access_token))
print(get_watts(access_token))
if __name__ == '__main__':
main()
This is what you get when you execute it directly
root@homeassistant:/usr/share/hassio/homeassistant# python3 custom_components/smart_control/smart_control_api.py
{'kWh': 8505}
{'power': 380}
This is my current data structure for the custom component:
root@homeassistant:/usr/share/hassio/homeassistant# tree custom_components/smart_control/
custom_components/smart_control/
|-- __init__.py
|-- __pycache__
| |-- __init__.cpython-39.pyc
| |-- sensor.cpython-39.pyc
| `-- smart_control_api.cpython-39.pyc
|-- manifest.json
|-- sensor.py
`-- smart_control_api.py
this is my current sensor.py
"""Platform for sensor integration."""
from __future__ import annotations
from homeassistant.components.sensor import (
SensorEntity,
STATE_CLASS_MEASUREMENT,
DEVICE_CLASS_ENERGY,
)
from homeassistant.const import ENERGY_KILO_WATT_HOUR
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .smart_control_api import get_token, get_watts, get_kWh
import requests, json
import os, time
from pathlib import Path
from datetime import datetime
from requests.structures import CaseInsensitiveDict
import logging
LOGGER = logging.getLogger(__name__)
USERNAME = "EMAIL"
PASSWORD = "PASSWORD"
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None
) -> None:
"""Set up the sensor platform."""
add_entities([
SmartControlEnergyConsumptionTotalSensor(),
SmartControlPowerConsumptionSensor()
])
class SmartControlEnergyConsumptionTotalSensor(SensorEntity):
"""Representation of a Smart Control Energy Consumption Total Sensor."""
_attr_name = "Smart Control Energy Consumption Total"
_attr_unit_of_measurement = ENERGY_KILO_WATT_HOUR
_attr_device_class = DEVICE_CLASS_ENERGY
_attr_state_class = STATE_CLASS_MEASUREMENT
def __init__(self):
self._attr_native_value = None
async def async_update(self):
"""Fetch new state data for the sensor."""
#self._attr_native_value = 1337
try:
access_token = get_token(USERNAME, PASSWORD)
LOGGER.debug("ACCESS_TOKEN", access_token)
kWh = get_kWh(access_token)
LOGGER.debug("kWh", kWh)
except requests.exceptions.ReadTimeout:
self._attr_native_value = None
class SmartControlPowerConsumptionSensor(SensorEntity):
"""Representation of a Smart Control Power Consumption Sensor."""
_attr_name = "Smart Control Power Consumption"
_attr_unit_of_measurement = "W"
def __init__(self):
self._attr_native_value = None
async def async_update(self):
"""Fetch new state data for the sensor."""
#self._attr_native_value = 69
try:
access_token = get_token(USERNAME, PASSWORD)
watts = get_watts(access_token)
self._attr_native_value = watts['power']
except requests.exceptions.ReadTimeout:
self._attr_native_value = None
So far, i tested the sensor with static values ( see commented lines with self._attr_native_value) and it worked without problems. But now, adding my functions to get the data it doesnt work. As far as i understood, it has to do with requests and async - i’m not that deep into programming and have no knowledge of asynchronous programming at all.
This is, what my logfile looks like:
2023-05-27 17:14:35 WARNING (MainThread) [homeassistant.util.async_] Detected blocking call to putrequest inside the event loop. This is causing stability issues. Please report issue to the custom component author for smart_control doing blocking calls at custom_components/smart_control/smart_control_api.py, line 55: response = requests.get(url, headers=headers, timeout=timeout)
2023-05-27 17:14:35 WARNING (MainThread) [homeassistant.util.async_] Detected blocking call to putrequest inside the event loop. This is causing stability issues. Please report issue to the custom component author for smart_control doing blocking calls at custom_components/smart_control/smart_control_api.py, line 45: response = requests.get(url, headers=headers, timeout=timeout)
2023-05-27 17:14:35 ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.smart_control_energy_consumption_total fails
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 535, in async_update_ha_state
await self.async_device_update()
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 744, in async_device_update
raise exc
File "/config/custom_components/smart_control/sensor.py", line 59, in async_update
kWh = get_kWh(access_token)
File "/config/custom_components/smart_control/smart_control_api.py", line 55, in get_kWh
response = requests.get(url, headers=headers, timeout=timeout)
File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 529, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 645, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 440, in send
resp = conn.urlopen(
File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 703, in urlopen
httplib_response = self._make_request(
File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 398, in _make_request
conn.request(method, url, **httplib_request_kw)
File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 239, in request
super(HTTPConnection, self).request(method, url, body=body, headers=headers)
File "/usr/local/lib/python3.9/http/client.py", line 1285, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/local/lib/python3.9/http/client.py", line 1296, in _send_request
self.putrequest(method, url, **skips)
File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 219, in putrequest
return _HTTPConnection.putrequest(self, method, url, *args, **kwargs)
File "/usr/src/homeassistant/homeassistant/util/async_.py", line 173, in protected_loop_func
check_loop(func, strict=strict)
File "/usr/src/homeassistant/homeassistant/util/async_.py", line 161, in check_loop
raise RuntimeError(
RuntimeError: Blocking calls must be done in the executor or a separate thread; Use `await hass.async_add_executor_job()` at custom_components/smart_control/smart_control_api.py, line 55: response = requests.get(url, headers=headers, timeout=timeout)
2023-05-27 17:14:35 ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.smart_control_power_consumption fails
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 535, in async_update_ha_state
await self.async_device_update()
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 744, in async_device_update
raise exc
File "/config/custom_components/smart_control/sensor.py", line 78, in async_update
watts = get_watts(access_token)
File "/config/custom_components/smart_control/smart_control_api.py", line 45, in get_watts
response = requests.get(url, headers=headers, timeout=timeout)
File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 529, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 645, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python3.9/site-packages/requests/adapters.py", line 440, in send
resp = conn.urlopen(
File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 703, in urlopen
httplib_response = self._make_request(
File "/usr/local/lib/python3.9/site-packages/urllib3/connectionpool.py", line 398, in _make_request
conn.request(method, url, **httplib_request_kw)
File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 239, in request
super(HTTPConnection, self).request(method, url, body=body, headers=headers)
File "/usr/local/lib/python3.9/http/client.py", line 1285, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/local/lib/python3.9/http/client.py", line 1296, in _send_request
self.putrequest(method, url, **skips)
File "/usr/local/lib/python3.9/site-packages/urllib3/connection.py", line 219, in putrequest
return _HTTPConnection.putrequest(self, method, url, *args, **kwargs)
File "/usr/src/homeassistant/homeassistant/util/async_.py", line 173, in protected_loop_func
check_loop(func, strict=strict)
File "/usr/src/homeassistant/homeassistant/util/async_.py", line 161, in check_loop
raise RuntimeError(
RuntimeError: Blocking calls must be done in the executor or a separate thread; Use `await hass.async_add_executor_job()` at custom_components/smart_control/smart_control_api.py, line 45: response = requests.get(url, headers=headers, timeout=timeout)
~
Any help is greatly appreciated
BR