Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,6 @@ omit =
homeassistant/components/sensor/haveibeenpwned.py
homeassistant/components/sensor/hp_ilo.py
homeassistant/components/sensor/htu21d.py
homeassistant/components/sensor/hydroquebec.py
homeassistant/components/sensor/imap.py
homeassistant/components/sensor/imap_email_content.py
homeassistant/components/sensor/influxdb.py
Expand Down
44 changes: 22 additions & 22 deletions homeassistant/components/sensor/hydroquebec.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.hydroquebec/
"""
import asyncio
import logging
from datetime import timedelta

import requests
import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
Expand All @@ -21,7 +21,7 @@
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['pyhydroquebec==1.3.1']
REQUIREMENTS = ['pyhydroquebec==2.0.2']

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -93,7 +93,8 @@
('yesterday_higher_price_consumption', 'consoHautQuot'))


def setup_platform(hass, config, add_devices, discovery_info=None):
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the HydroQuebec sensor."""
# Create a data fetcher to support all of the configured sensors. Then make
# the first call to init the data.
Expand All @@ -102,21 +103,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
password = config.get(CONF_PASSWORD)
contract = config.get(CONF_CONTRACT)

try:
hydroquebec_data = HydroquebecData(username, password, contract)
_LOGGER.info("Contract list: %s",
", ".join(hydroquebec_data.get_contract_list()))
except requests.exceptions.HTTPError as error:
_LOGGER.error("Failt login: %s", error)
return False
hydroquebec_data = HydroquebecData(username, password, contract)
contracts = yield from hydroquebec_data.get_contract_list()
_LOGGER.info("Contract list: %s",
", ".join(contracts))

name = config.get(CONF_NAME)

sensors = []
for variable in config[CONF_MONITORED_VARIABLES]:
sensors.append(HydroQuebecSensor(hydroquebec_data, variable, name))

add_devices(sensors)
async_add_devices(sensors, True)


class HydroQuebecSensor(Entity):
Expand Down Expand Up @@ -152,10 +150,11 @@ def icon(self):
"""Icon to use in the frontend, if any."""
return self._icon

def update(self):
@asyncio.coroutine
def async_update(self):
"""Get the latest data from Hydroquebec and update the state."""
self.hydroquebec_data.update()
if self.type in self.hydroquebec_data.data:
yield from self.hydroquebec_data.async_update()
if self.hydroquebec_data.data.get(self.type) is not None:
self._state = round(self.hydroquebec_data.data[self.type], 2)


Expand All @@ -170,23 +169,24 @@ def __init__(self, username, password, contract=None):
self._contract = contract
self.data = {}

@asyncio.coroutine
def get_contract_list(self):
"""Return the contract list."""
# Fetch data
self._fetch_data()
yield from self._fetch_data()
return self.client.get_contracts()

@asyncio.coroutine
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def _fetch_data(self):
"""Fetch latest data from HydroQuebec."""
from pyhydroquebec.client import PyHydroQuebecError
try:
self.client.fetch_data()
except PyHydroQuebecError as exp:
yield from self.client.fetch_data()
except BaseException as exp:
_LOGGER.error("Error on receive last Hydroquebec data: %s", exp)
return

@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
@asyncio.coroutine
def async_update(self):
"""Return the latest collected data from HydroQuebec."""
self._fetch_data()
yield from self._fetch_data()
self.data = self.client.get_data(self._contract)[self._contract]
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ pyhiveapi==0.2.5
pyhomematic==0.1.35

# homeassistant.components.sensor.hydroquebec
pyhydroquebec==1.3.1
pyhydroquebec==2.0.2

# homeassistant.components.alarm_control_panel.ialarm
pyialarm==0.2
Expand Down
89 changes: 89 additions & 0 deletions tests/components/sensor/test_hydroquebec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""The test for the hydroquebec sensor platform."""
import asyncio
import logging
import sys
from unittest.mock import MagicMock

from homeassistant.bootstrap import async_setup_component
from homeassistant.components.sensor import hydroquebec
from tests.common import assert_setup_component


CONTRACT = "123456789"


class HydroQuebecClientMock():
"""Fake Hydroquebec client."""

def __init__(self, username, password, contract=None):
"""Fake Hydroquebec client init."""
pass

def get_data(self, contract):
"""Return fake hydroquebec data."""
return {CONTRACT: {"balance": 160.12}}

def get_contracts(self):
"""Return fake hydroquebec contracts."""
return [CONTRACT]

@asyncio.coroutine
def fetch_data(self):
"""Return fake fetching data."""
pass


class HydroQuebecClientMockError(HydroQuebecClientMock):
"""Fake Hydroquebec client error."""

@asyncio.coroutine
def fetch_data(self):
"""Return fake fetching data."""
raise hydroquebec.PyHydroQuebecError("Fake Error")


class PyHydroQuebecErrorMock(BaseException):
"""Fake PyHydroquebec Error."""


@asyncio.coroutine
def test_hydroquebec_sensor(loop, hass):
"""Test the Hydroquebec number sensor."""
sys.modules['pyhydroquebec'] = MagicMock()
sys.modules['pyhydroquebec.client'] = MagicMock()
sys.modules['pyhydroquebec.client.PyHydroQuebecError'] = \
PyHydroQuebecErrorMock
import pyhydroquebec.client
pyhydroquebec.HydroQuebecClient = HydroQuebecClientMock
pyhydroquebec.client.PyHydroQuebecError = PyHydroQuebecErrorMock
config = {
'sensor': {
'platform': 'hydroquebec',
'name': 'hydro',
'contract': CONTRACT,
'username': 'myusername',
'password': 'password',
'monitored_variables': [
'balance',
],
}
}
with assert_setup_component(1):
yield from async_setup_component(hass, 'sensor', config)
state = hass.states.get('sensor.hydro_balance')
assert state.state == "160.12"
assert state.attributes.get('unit_of_measurement') == "CAD"


@asyncio.coroutine
def test_error(hass, caplog):
"""Test the Hydroquebec sensor errors."""
caplog.set_level(logging.ERROR)
sys.modules['pyhydroquebec'] = MagicMock()
sys.modules['pyhydroquebec.client'] = MagicMock()
import pyhydroquebec.client
pyhydroquebec.HydroQuebecClient = HydroQuebecClientMockError
pyhydroquebec.client.PyHydroQuebecError = BaseException
hydro_data = hydroquebec.HydroquebecData('username', 'password')
yield from hydro_data._fetch_data()
assert "Error on receive last Hydroquebec data: " in caplog.text