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

Add binary sensors for sense energy monitor #17645

Merged
merged 40 commits into from Nov 2, 2018
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1ee1090
Added error handling for sense API timeouts
kbickar Aug 2, 2018
5cff9df
Moved imports in function
kbickar Aug 2, 2018
cb7b273
Moved imports to more appropriate function
kbickar Aug 2, 2018
3fa4d0d
Change exception to custom package version
kbickar Aug 12, 2018
f6345ff
Merge remote-tracking branch 'upstream/dev' into dev
kbickar Sep 5, 2018
51c99d7
Updated sense_energy library to 0.4.2
kbickar Sep 5, 2018
ac498fc
Added binary sensors for individual devices
kbickar Oct 20, 2018
538713e
Merge remote-tracking branch 'upstream/dev' into dev
kbickar Oct 20, 2018
56fe11a
Whitespace updates
kbickar Oct 20, 2018
92b2c83
Split into component, sensors, binary sensors
kbickar Oct 23, 2018
37993f8
Fixed whitespace
kbickar Oct 23, 2018
d2b2955
Fixed whitespace
kbickar Oct 23, 2018
6ddb790
Moved time constant into sensor file
kbickar Oct 23, 2018
e9523d2
Regenerated requirements
kbickar Oct 23, 2018
6cac4bb
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
kbickar Oct 23, 2018
31948dd
Fixed whitespace
kbickar Oct 23, 2018
f8d3219
Updated component dependency
kbickar Oct 23, 2018
704a66c
Fixed whitespace
kbickar Oct 23, 2018
ebe233c
Code cleanup
kbickar Oct 23, 2018
66b33dc
High and low target temps are also supported if target is supported
kbickar Oct 27, 2018
5acd425
Revert "High and low target temps are also supported if target is sup…
kbickar Oct 27, 2018
c411464
Added all sense components to .coveragerc
kbickar Oct 30, 2018
9307d02
Added check authentication exception
kbickar Oct 31, 2018
8e6dfc2
binary/sensor platforms loaded in setup
kbickar Oct 31, 2018
a018c6f
Changed to add all detected devices
kbickar Oct 31, 2018
f506310
Changed to add all sensors on setup
kbickar Oct 31, 2018
9320209
Code cleanup
kbickar Nov 1, 2018
b80c6b7
Whitespace
kbickar Nov 1, 2018
59d16b6
Whitespace
kbickar Nov 1, 2018
84bb868
Added sense as requirement for platform
kbickar Nov 1, 2018
3513114
pylint fixes
kbickar Nov 1, 2018
a8dcbea
Whitespace
kbickar Nov 1, 2018
dc58bda
Switched requirement to dependency
kbickar Nov 1, 2018
62d5a6a
Made non-class function
kbickar Nov 1, 2018
5a90b96
Whitespace
kbickar Nov 1, 2018
d3830a5
Removed unneeded checks
kbickar Nov 1, 2018
d0e8724
Increased API delay to 60 seconds
kbickar Nov 1, 2018
74207f9
Added guard clause for discovery_info
kbickar Nov 1, 2018
746420b
Tidy code
kbickar Nov 1, 2018
d4bbfe4
Whitespace
kbickar Nov 1, 2018
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
4 changes: 3 additions & 1 deletion .coveragerc
Expand Up @@ -289,6 +289,9 @@ omit =
homeassistant/components/scsgate.py
homeassistant/components/*/scsgate.py

homeassistant/components/sense.py
homeassistant/components/*/sense.py

homeassistant/components/simplisafe/__init__.py
homeassistant/components/*/simplisafe.py

Expand Down Expand Up @@ -758,7 +761,6 @@ omit =
homeassistant/components/sensor/ripple.py
homeassistant/components/sensor/rtorrent.py
homeassistant/components/sensor/scrape.py
homeassistant/components/sensor/sense.py
homeassistant/components/sensor/sensehat.py
homeassistant/components/sensor/serial_pm.py
homeassistant/components/sensor/serial.py
Expand Down
117 changes: 117 additions & 0 deletions homeassistant/components/binary_sensor/sense.py
@@ -0,0 +1,117 @@
"""
Support for monitoring a Sense energy sensor device.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.sense/
"""
import logging

from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.sense import SENSE_DATA

DEPENDENCIES = ['sense']

_LOGGER = logging.getLogger(__name__)

BIN_SENSOR_CLASS = 'power'
MDI_ICONS = {'ac': 'air-conditioner',
'aquarium': 'fish',
'car': 'car-electric',
'computer': 'desktop-classic',
'cup': 'coffee',
'dehumidifier': 'water-off',
'dishes': 'dishwasher',
'drill': 'toolbox',
'fan': 'fan',
'freezer': 'fridge-top',
'fridge': 'fridge-bottom',
'game': 'gamepad-variant',
'garage': 'garage',
'grill': 'stove',
'heat': 'fire',
'heater': 'radiatior',
'humidifier': 'water',
'kettle': 'kettle',
'leafblower': 'leaf',
'lightbulb': 'lightbulb',
'media_console': 'set-top-box',
'modem': 'router-wireless',
'outlet': 'power-socket-us',
'papershredder': 'shredder',
'printer': 'printer',
'pump': 'water-pump',
'settings': 'settings',
'skillet': 'pot',
'smartcamera': 'webcam',
'socket': 'power-plug',
'sound': 'speaker',
'stove': 'stove',
'trash': 'trash-can',
'tv': 'television',
'vacuum': 'robot-vacuum',
'washer': 'washing-machine'}


def setup_platform(hass, config, add_entities, discovery_info=None):
kbickar marked this conversation as resolved.
Show resolved Hide resolved
"""Set up the Sense sensor."""
if SENSE_DATA not in hass.data:
kbickar marked this conversation as resolved.
Show resolved Hide resolved
_LOGGER.error("Requires Sense component loaded")
return False

data = hass.data[SENSE_DATA]
kbickar marked this conversation as resolved.
Show resolved Hide resolved

sense_devices = data.get_discovered_device_data()
devices = [SenseDevice(data, device) for device in sense_devices]
add_entities(devices)


def sense_to_mdi(sense_icon):
"""Convert sense icon to mdi icon."""
return 'mdi-' + MDI_ICONS.get(sense_icon, 'power-plug')
kbickar marked this conversation as resolved.
Show resolved Hide resolved


class SenseDevice(BinarySensorDevice):
"""Implementation of a Sense energy device binary sensor."""

def __init__(self, data, device):
"""Initialize the sensor."""
self._name = device['name']
self._id = device['id']
self._icon = sense_to_mdi(device['icon'])
self._data = data
self._state = False

@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self._state

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

@property
def unique_id(self):
"""Return the id of the binary sensor."""
return self._id

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

@property
def device_class(self):
"""Return the device class of the binary sensor."""
return BIN_SENSOR_CLASS

def update(self):
"""Retrieve latest state."""
from sense_energy.sense_api import SenseAPITimeoutException
try:
self._data.get_realtime()
kbickar marked this conversation as resolved.
Show resolved Hide resolved
except SenseAPITimeoutException:
_LOGGER.error("Timeout retrieving data")
return
self._state = self._name in self._data.active_devices
53 changes: 53 additions & 0 deletions homeassistant/components/sense.py
@@ -0,0 +1,53 @@
"""
Support for monitoring a Sense energy sensor.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sense/
"""
import logging

import voluptuous as vol
kbickar marked this conversation as resolved.
Show resolved Hide resolved

from homeassistant.helpers.discovery import load_platform
from homeassistant.const import (CONF_EMAIL, CONF_PASSWORD, CONF_TIMEOUT)
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['sense_energy==0.5.1']

_LOGGER = logging.getLogger(__name__)

SENSE_DATA = 'sense_data'

DOMAIN = 'sense'

ACTIVE_UPDATE_RATE = 30
DEFAULT_TIMEOUT = 5

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_EMAIL): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_TIMEOUT, DEFAULT_TIMEOUT): cv.positive_int,
})
}, extra=vol.ALLOW_EXTRA)


def setup(hass, config):
"""Set up the Sense sensor."""
from sense_energy import Senseable, SenseAuthenticationException

username = config[DOMAIN][CONF_EMAIL]
password = config[DOMAIN][CONF_PASSWORD]

timeout = config[DOMAIN][CONF_TIMEOUT]
try:
hass.data[SENSE_DATA] = Senseable(api_timeout=timeout,
wss_timeout=timeout)
hass.data[SENSE_DATA].authenticate(username, password)
hass.data[SENSE_DATA].rate_limit = ACTIVE_UPDATE_RATE
except SenseAuthenticationException:
_LOGGER.error("Could not authenticate with sense server")
return False
load_platform(hass, 'sensor', DOMAIN, {}, config)
load_platform(hass, 'binary_sensor', DOMAIN, {}, config)
return True
62 changes: 21 additions & 41 deletions homeassistant/components/sensor/sense.py
Expand Up @@ -5,24 +5,20 @@
https://home-assistant.io/components/sensor.sense/
"""
import logging
from datetime import timedelta

import voluptuous as vol
from datetime import timedelta

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (CONF_EMAIL, CONF_PASSWORD,
CONF_MONITORED_CONDITIONS)
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
import homeassistant.helpers.config_validation as cv
from homeassistant.components.sense import SENSE_DATA

REQUIREMENTS = ['sense_energy==0.4.2']
DEPENDENCIES = ['sense']

_LOGGER = logging.getLogger(__name__)

ACTIVE_NAME = "Energy"
PRODUCTION_NAME = "Production"
CONSUMPTION_NAME = "Usage"
ACTIVE_NAME = 'Energy'
PRODUCTION_NAME = 'Production'
CONSUMPTION_NAME = 'Usage'

ACTIVE_TYPE = 'active'

Expand All @@ -46,55 +42,39 @@ def __init__(self, name, sensor_type):
# Production/consumption variants
SENSOR_VARIANTS = [PRODUCTION_NAME.lower(), CONSUMPTION_NAME.lower()]

# Valid sensors for configuration
VALID_SENSORS = ['%s_%s' % (typ, var)
for typ in SENSOR_TYPES
for var in SENSOR_VARIANTS]

ICON = 'mdi:flash'

MIN_TIME_BETWEEN_DAILY_UPDATES = timedelta(seconds=300)
MIN_TIME_BETWEEN_ACTIVE_UPDATES = timedelta(seconds=60)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_EMAIL): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_MONITORED_CONDITIONS):
vol.All(cv.ensure_list, vol.Length(min=1), [vol.In(VALID_SENSORS)]),
})


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Sense sensor."""
from sense_energy import Senseable

username = config.get(CONF_EMAIL)
password = config.get(CONF_PASSWORD)

data = Senseable(username, password)
if SENSE_DATA not in hass.data:
kbickar marked this conversation as resolved.
Show resolved Hide resolved
_LOGGER.error("Requires Sense component loaded")
return False
data = hass.data[SENSE_DATA]
kbickar marked this conversation as resolved.
Show resolved Hide resolved

@Throttle(MIN_TIME_BETWEEN_DAILY_UPDATES)
def update_trends():
"""Update the daily power usage."""
data.update_trend_data()

@Throttle(MIN_TIME_BETWEEN_ACTIVE_UPDATES)
def update_active():
"""Update the active power usage."""
data.get_realtime()

devices = []
for sensor in config.get(CONF_MONITORED_CONDITIONS):
config_name, prod = sensor.rsplit('_', 1)
name = SENSOR_TYPES[config_name].name
sensor_type = SENSOR_TYPES[config_name].sensor_type
is_production = prod == PRODUCTION_NAME.lower()
if sensor_type == ACTIVE_TYPE:
update_call = update_active
else:
update_call = update_trends
devices.append(Sense(data, name, sensor_type,
is_production, update_call))
for typ in SENSOR_TYPES.values():
for var in SENSOR_VARIANTS:
name = typ.name
sensor_type = typ.sensor_type
is_production = var == PRODUCTION_NAME.lower()
if sensor_type == ACTIVE_TYPE:
update_call = update_active
else:
update_call = update_trends
devices.append(Sense(data, name, sensor_type,
is_production, update_call))

add_entities(devices)

Expand Down
4 changes: 2 additions & 2 deletions requirements_all.txt
Expand Up @@ -1338,8 +1338,8 @@ sendgrid==5.6.0
# homeassistant.components.sensor.sensehat
sense-hat==2.2.0

# homeassistant.components.sensor.sense
sense_energy==0.4.2
# homeassistant.components.sense
sense_energy==0.5.1

# homeassistant.components.media_player.aquostv
sharp_aquos_rc==0.3.2
Expand Down