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

Suez water #23844

Merged
merged 38 commits into from Jul 23, 2019
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ca3df87
Add suez water sensor
May 10, 2019
6060924
flake8 test
May 10, 2019
10d465c
pylint test
May 10, 2019
ae64f79
edition to fix flake8 and pylint issues
May 10, 2019
5e7542c
edition to be okay with the musts
May 10, 2019
900a0ba
Added a blank line to __init.py__ for flake8
May 10, 2019
e46b070
added blank line for flake8
May 13, 2019
f43247b
Merge branch 'suezWater' of https://github.com/ooii/home-assistant in…
May 13, 2019
764d94b
changer scan interval from 10 to 720 minutes
May 13, 2019
3fedfec
use of pysuez
Jun 3, 2019
ee24baa
bug fix and isort
Jun 3, 2019
74d799e
use of pysuez
Jun 4, 2019
a80391e
fixed flake8 and pylint errors
Jun 4, 2019
61a9695
update requirements_all.txt
Jun 4, 2019
0f20749
added a method to test login/password befire adding device
Jun 4, 2019
6390bff
flake8 edition
Jun 4, 2019
0386e2d
update requirements_all.txt
Jun 4, 2019
a9031a4
add of .coveragerc file with untested files
Jun 5, 2019
8573e43
update of .coveragerc
Jun 5, 2019
b944137
Update homeassistant/components/suez_water/__init__.py
ooii Jun 7, 2019
3394a21
Update homeassistant/components/suez_water/sensor.py
ooii Jun 7, 2019
32c68b9
Update homeassistant/components/suez_water/sensor.py
ooii Jun 7, 2019
5f4189d
Update homeassistant/components/suez_water/sensor.py
ooii Jun 7, 2019
a90df5c
Update homeassistant/components/suez_water/sensor.py
ooii Jun 7, 2019
65ff9c7
Update homeassistant/components/suez_water/sensor.py
ooii Jun 7, 2019
7878697
bug fix in check credentials
Jun 7, 2019
ebd5ac1
flake8 and pylint fixes
Jun 7, 2019
a781c95
fix codeowner
Jun 8, 2019
37232e3
update requirements_all.txt
Jun 8, 2019
8db21b2
Merge branch 'dev' into suezWater
ooii Jun 11, 2019
d636f79
Merge branch 'dev' into suezWater
ooii Jun 25, 2019
993471f
Sorted suez_water line
ooii Jul 3, 2019
2db8648
edition to answer comments from @MartinHjelmare on #23844
Jul 3, 2019
50e74df
Merge branch 'suezWater' of https://github.com/ooii/home-assistant in…
Jul 3, 2019
af38c6e
Attribute keys formatting to lowercase snakecase, name and icon const…
Jul 3, 2019
445e69d
pylint edition
Jul 4, 2019
d3a2263
correction wrong keys in client attributes
Jul 4, 2019
1726da1
remove of unnedeed return and move add_entities
Jul 23, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Empty file.
1 change: 1 addition & 0 deletions homeassistant/components/suez_water/__init__.py
@@ -0,0 +1 @@
"""France Suez Water Consumption Sensor."""
ooii marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 8 additions & 0 deletions homeassistant/components/suez_water/manifest.json
@@ -0,0 +1,8 @@
{
"domain": "suez_water",
"name": "Compteur d'eau Suez",
ooii marked this conversation as resolved.
Show resolved Hide resolved
"documentation": "https://github.com/home-assistant/example-custom-config/tree/master/custom_components/example_sensor/",
"dependencies": [],
"codeowners": [],
ooii marked this conversation as resolved.
Show resolved Hide resolved
"requirements": ["requests==2.21.0", "regex==2019.4.14", "voluptuous==0.11.5", "datetime==4.3"]
}
238 changes: 238 additions & 0 deletions homeassistant/components/suez_water/sensor.py
@@ -0,0 +1,238 @@
"""Sensor for Suez Water Consumption data."""
from datetime import timedelta
import logging

import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, VOLUME_LITERS
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity

_LOGGER = logging.getLogger(__name__)
CONF_COUNTER_ID = 'counter_id'

MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=720)
ooii marked this conversation as resolved.
Show resolved Hide resolved
SCAN_INTERVAL = timedelta(minutes=720)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_COUNTER_ID): cv.string,
})


def setup_platform(hass, config, add_devices, discovery_info=None):
ooii marked this conversation as resolved.
Show resolved Hide resolved
"""Set up the sensor platform."""
username = config.get(CONF_USERNAME)
ooii marked this conversation as resolved.
Show resolved Hide resolved
password = config.get(CONF_PASSWORD)
counter_id = config.get(CONF_COUNTER_ID)
_LOGGER.debug(
"Username is %s and counter_id is %s.",
ooii marked this conversation as resolved.
Show resolved Hide resolved
username, counter_id
)
add_devices([SuezClient(username, password, counter_id)], True)
ooii marked this conversation as resolved.
Show resolved Hide resolved


class SuezClient(Entity):
"""Global variables."""

BASE_URI = 'https://www.toutsurmoneau.fr'
API_ENDPOINT_LOGIN = '/mon-compte-en-ligne/je-me-connecte'
API_ENDPOINT_DATA = '/mon-compte-en-ligne/statJData/'
API_ENDPOINT_HISTORY = '/mon-compte-en-ligne/statMData/'

"""Representation of a Sensor."""

def __init__(self, username, password, counter_id):
"""Initialize the data object."""
self._name = "Suez Water Client"
ooii marked this conversation as resolved.
Show resolved Hide resolved
self._username = username
self._password = password
self._counter_id = counter_id
self._token = ''
self._headers = {}
self._attributes = {}
self.data = {}
self.success = False
ooii marked this conversation as resolved.
Show resolved Hide resolved
self._state = 0
ooii marked this conversation as resolved.
Show resolved Hide resolved
self._icon = 'mdi:water-pump'
ooii marked this conversation as resolved.
Show resolved Hide resolved

@property
def name(self):
"""Return the name of the sensor."""
return self._name

@property
def state(self):
"""Return the state of the sensor."""
return self._state

@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return VOLUME_LITERS

@property
def state_attributes(self):
ooii marked this conversation as resolved.
Show resolved Hide resolved
"""Return the state attributes."""
return self._attributes
ooii marked this conversation as resolved.
Show resolved Hide resolved

@property
def icon(self):
"""Return the icon of the sensor."""
return self._icon

def _get_token(self):
import requests
import re
headers = {
'Accept': "application/json, text/javascript, */*; q=0.01",
'Content-Type': 'application/x-www-form-urlencoded',
'Accept-Language': 'fr,fr-FR;q=0.8,en;q=0.6',
'User-Agent': 'curl/7.54.0',
'Connection': 'keep-alive',
'Cookie': ''
}

url = self.BASE_URI+self.API_ENDPOINT_LOGIN

response = requests.get(url, headers=headers)
ooii marked this conversation as resolved.
Show resolved Hide resolved

headers['Cookie'] = ""
for key in response.cookies.get_dict():
if headers['Cookie']:
headers['Cookie'] += "; "
headers['Cookie'] += key + "=" + response.cookies[key]

phrase = re.compile('_csrf_token" value="(.*)" />')
result = phrase.search(response.content.decode('utf-8'))
self._token = result.group(1)
_LOGGER.debug("Le token is %s", self._token)
self._headers = headers

def _get_cookie(self):
import requests
login = requests.Session()
data = {
'_username': self._username,
'_password': self._password,
'_csrf_token': self._token,
'signin[username]': self._username,
'signin[password]': None,
'tsme_user_login[_username]': self._username,
'tsme_user_login[_password]': self._password
}
url = self.BASE_URI+self.API_ENDPOINT_LOGIN
login.post(url, headers=self._headers, data=data)
_LOGGER.debug("Cookie is %s", login.cookies.get("eZSESSID"))
self._headers['Cookie'] = ''
self._headers['Cookie'] = 'eZSESSID='+login.cookies.get("eZSESSID")

def _fetch_data(self):
"""Fetch latest data from Suez."""
import datetime
import requests
now = datetime.datetime.now()
today_year = now.strftime("%Y")
today_month = now.strftime("%m")
yesterday = datetime.datetime.now() - datetime.timedelta(1)
yesterday_year = yesterday.strftime('%Y')
yesterday_month = yesterday.strftime('%m')
yesterday_day = yesterday.strftime('%d')
url = self.BASE_URI+self.API_ENDPOINT_DATA
url += '{}/{}/{}'.format(
yesterday_year,
yesterday_month, self._counter_id
)

data = requests.get(url, headers=self._headers)

try:
self._state = int(float(data.json()[int(
yesterday_day)-1][1])*1000)
self.success = True

except ValueError:
_LOGGER.debug("Issue with this yesterday data")
pass

try:
if yesterday_month != today_month:
url = self.BASE_URI+self.API_ENDPOINT_DATA
url += '{}/{}/{}'.format(
today_year,
today_month, self._counter_id
)
_LOGGER.debug("Getting data for previous month")
data = requests.get(url, headers=self._headers)

self._attributes['thisMonthConsumption'] = {}
ooii marked this conversation as resolved.
Show resolved Hide resolved
for item in data.json():
self._attributes['thisMonthConsumption'][item[0]] = int(
float(item[1])*1000)

except ValueError:
_LOGGER.debug("Issue with this month data")
pass

try:
if int(today_month) == 1:
last_month = 12
last_month_year = int(today_year) - 1
else:
last_month = int(today_month) - 1
last_month_year = today_year

url = self.BASE_URI+self.API_ENDPOINT_DATA
url += '{}/{}/{}'.format(
last_month_year, last_month,
self._counter_id
)

_LOGGER.debug("Getting data for previous month")
data = requests.get(url, headers=self._headers)

self._attributes['previousMonthConsumption'] = {}
for item in data.json():
self._attributes['previousMonthConsumption'][item[0]] = int(
float(item[1])*1000)

except ValueError:
_LOGGER.debug("Issue with this previous month data")
pass

try:
url = self.BASE_URI+self.API_ENDPOINT_HISTORY
url += '{}'.format(self._counter_id)

data = requests.get(url, headers=self._headers)
fetched_data = data.json()
self._attributes['highestMonthlyConsumption'] = int(
float(fetched_data[-1])*1000)
fetched_data.pop()
self._attributes['lastYearOverAll'] = int(
float(fetched_data[-1])*1000)
fetched_data.pop()
self._attributes['thisYearOverAll'] = int(
float(fetched_data[-1])*1000)
fetched_data.pop()
self._attributes['history'] = {}
for item in fetched_data:
self._attributes['history'][item[3]] = int(
float(item[1])*1000)

_LOGGER.debug("_attributes est %s", self._attributes)

except ValueError:
_LOGGER.debug("Issue with history data")
raise

def update(self):
"""Return the latest collected data from Linky."""
self._get_token()
self._get_cookie()
self._fetch_data()
if not self.success:
return
_LOGGER.debug("Suez data state is: %s", self._state)
12 changes: 12 additions & 0 deletions requirements_all.txt
Expand Up @@ -325,6 +325,9 @@ datadog==0.15.0
# homeassistant.components.metoffice
datapoint==0.4.3

# homeassistant.components.suez_water
datetime==4.3

# homeassistant.components.decora
# decora==0.6

Expand Down Expand Up @@ -1527,6 +1530,12 @@ recollect-waste==1.0.1
# homeassistant.components.rainmachine
regenmaschine==1.4.0

# homeassistant.components.suez_water
regex==2019.4.14

# homeassistant.components.suez_water
requests==2.21.0

# homeassistant.components.python_script
restrictedpython==4.0b8

Expand Down Expand Up @@ -1766,6 +1775,9 @@ venstarcolortouch==0.6
# homeassistant.components.volkszaehler
volkszaehler==0.1.2

# homeassistant.components.suez_water
voluptuous==0.11.5

# homeassistant.components.volvooncall
volvooncall==0.8.7

Expand Down