diff --git a/.coveragerc b/.coveragerc index 60c2df14cea..7934fe64658 100644 --- a/.coveragerc +++ b/.coveragerc @@ -574,6 +574,7 @@ omit = homeassistant/components/starlingbank/sensor.py homeassistant/components/steam_online/sensor.py homeassistant/components/stiebel_eltron/* + homeassistant/components/streamlabswater/* homeassistant/components/stride/notify.py homeassistant/components/supervisord/sensor.py homeassistant/components/swiss_hydrological_data/sensor.py diff --git a/homeassistant/components/streamlabswater/__init__.py b/homeassistant/components/streamlabswater/__init__.py new file mode 100644 index 00000000000..7e4fdd855c3 --- /dev/null +++ b/homeassistant/components/streamlabswater/__init__.py @@ -0,0 +1,84 @@ +"""Support for Streamlabs Water Monitor devices.""" +import logging + +import voluptuous as vol + +from homeassistant.const import CONF_API_KEY +from homeassistant.helpers import discovery +import homeassistant.helpers.config_validation as cv + +DOMAIN = 'streamlabswater' + +_LOGGER = logging.getLogger(__name__) + +ATTR_AWAY_MODE = 'away_mode' +SERVICE_SET_AWAY_MODE = 'set_away_mode' +AWAY_MODE_AWAY = 'away' +AWAY_MODE_HOME = 'home' + +STREAMLABSWATER_COMPONENTS = [ + 'sensor', 'binary_sensor' +] + +CONF_LOCATION_ID = "location_id" + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_API_KEY): cv.string, + vol.Optional(CONF_LOCATION_ID): cv.string + }) +}, extra=vol.ALLOW_EXTRA) + +SET_AWAY_MODE_SCHEMA = vol.Schema({ + vol.Required(ATTR_AWAY_MODE): vol.In([AWAY_MODE_AWAY, AWAY_MODE_HOME]) +}) + + +def setup(hass, config): + """Set up the streamlabs water component.""" + from streamlabswater import streamlabswater + + conf = config[DOMAIN] + api_key = conf.get(CONF_API_KEY) + location_id = conf.get(CONF_LOCATION_ID) + + client = streamlabswater.StreamlabsClient(api_key) + locations = client.get_locations().get('locations') + + if locations is None: + _LOGGER.error("Unable to retrieve locations. Verify API key") + return False + + if location_id is None: + location = locations[0] + location_id = location['locationId'] + _LOGGER.info("Streamlabs Water Monitor auto-detected location_id=%s", + location_id) + else: + location = next(( + l for l in locations if location_id == l['locationId']), None) + if location is None: + _LOGGER.error("Supplied location_id is invalid") + return False + + location_name = location['name'] + + hass.data[DOMAIN] = { + 'client': client, + 'location_id': location_id, + 'location_name': location_name + } + + for component in STREAMLABSWATER_COMPONENTS: + discovery.load_platform(hass, component, DOMAIN, {}, config) + + def set_away_mode(service): + """Set the StreamLabsWater Away Mode.""" + away_mode = service.data.get(ATTR_AWAY_MODE) + client.update_location(location_id, away_mode) + + hass.services.register( + DOMAIN, SERVICE_SET_AWAY_MODE, set_away_mode, + schema=SET_AWAY_MODE_SCHEMA) + + return True diff --git a/homeassistant/components/streamlabswater/binary_sensor.py b/homeassistant/components/streamlabswater/binary_sensor.py new file mode 100644 index 00000000000..d6351cc2dc6 --- /dev/null +++ b/homeassistant/components/streamlabswater/binary_sensor.py @@ -0,0 +1,73 @@ +"""Support for Streamlabs Water Monitor Away Mode.""" + +from datetime import timedelta + +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.components.streamlabswater import ( + DOMAIN as STREAMLABSWATER_DOMAIN) +from homeassistant.util import Throttle + +DEPENDS = ['streamlabswater'] + +MIN_TIME_BETWEEN_LOCATION_UPDATES = timedelta(seconds=60) + +ATTR_LOCATION_ID = "location_id" +NAME_AWAY_MODE = "Water Away Mode" + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the StreamLabsWater mode sensor.""" + client = hass.data[STREAMLABSWATER_DOMAIN]['client'] + location_id = hass.data[STREAMLABSWATER_DOMAIN]['location_id'] + location_name = hass.data[STREAMLABSWATER_DOMAIN]['location_name'] + + streamlabs_location_data = StreamlabsLocationData(location_id, client) + streamlabs_location_data.update() + + add_devices([ + StreamlabsAwayMode(location_name, streamlabs_location_data) + ]) + + +class StreamlabsLocationData: + """Track and query location data.""" + + def __init__(self, location_id, client): + """Initialize the location data.""" + self._location_id = location_id + self._client = client + self._is_away = None + + @Throttle(MIN_TIME_BETWEEN_LOCATION_UPDATES) + def update(self): + """Query and store location data.""" + location = self._client.get_location(self._location_id) + self._is_away = location['homeAway'] == 'away' + + def is_away(self): + """Return whether away more is enabled.""" + return self._is_away + + +class StreamlabsAwayMode(BinarySensorDevice): + """Monitor the away mode state.""" + + def __init__(self, location_name, streamlabs_location_data): + """Initialize the away mode device.""" + self._location_name = location_name + self._streamlabs_location_data = streamlabs_location_data + self._is_away = None + + @property + def name(self): + """Return the name for away mode.""" + return "{} {}".format(self._location_name, NAME_AWAY_MODE) + + @property + def is_on(self): + """Return if away mode is on.""" + return self._streamlabs_location_data.is_away() + + def update(self): + """Retrieve the latest location data and away mode state.""" + self._streamlabs_location_data.update() diff --git a/homeassistant/components/streamlabswater/manifest.json b/homeassistant/components/streamlabswater/manifest.json new file mode 100644 index 00000000000..b4173ebf0e9 --- /dev/null +++ b/homeassistant/components/streamlabswater/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "streamlabswater", + "name": "Streamlabs Water", + "documentation": "https://www.home-assistant.io/components/streamlabswater", + "requirements": [ + "streamlabswater==1.0.1" + ], + "dependencies": [], + "codeowners": [] +} diff --git a/homeassistant/components/streamlabswater/sensor.py b/homeassistant/components/streamlabswater/sensor.py new file mode 100644 index 00000000000..9d55b4931ad --- /dev/null +++ b/homeassistant/components/streamlabswater/sensor.py @@ -0,0 +1,128 @@ +"""Support for Streamlabs Water Monitor Usage.""" + +from datetime import timedelta + +from homeassistant.components.streamlabswater import ( + DOMAIN as STREAMLABSWATER_DOMAIN) +from homeassistant.const import VOLUME_GALLONS +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle + +DEPENDENCIES = ['streamlabswater'] + +WATER_ICON = 'mdi:water' +MIN_TIME_BETWEEN_USAGE_UPDATES = timedelta(seconds=60) + +NAME_DAILY_USAGE = "Daily Water" +NAME_MONTHLY_USAGE = "Monthly Water" +NAME_YEARLY_USAGE = "Yearly Water" + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up water usage sensors.""" + client = hass.data[STREAMLABSWATER_DOMAIN]['client'] + location_id = hass.data[STREAMLABSWATER_DOMAIN]['location_id'] + location_name = hass.data[STREAMLABSWATER_DOMAIN]['location_name'] + + streamlabs_usage_data = StreamlabsUsageData(location_id, client) + streamlabs_usage_data.update() + + add_devices([ + StreamLabsDailyUsage(location_name, streamlabs_usage_data), + StreamLabsMonthlyUsage(location_name, streamlabs_usage_data), + StreamLabsYearlyUsage(location_name, streamlabs_usage_data) + ]) + + +class StreamlabsUsageData: + """Track and query usage data.""" + + def __init__(self, location_id, client): + """Initialize the usage data.""" + self._location_id = location_id + self._client = client + self._today = None + self._this_month = None + self._this_year = None + + @Throttle(MIN_TIME_BETWEEN_USAGE_UPDATES) + def update(self): + """Query and store usage data.""" + water_usage = self._client.get_water_usage_summary(self._location_id) + self._today = round(water_usage['today'], 1) + self._this_month = round(water_usage['thisMonth'], 1) + self._this_year = round(water_usage['thisYear'], 1) + + def get_daily_usage(self): + """Return the day's usage.""" + return self._today + + def get_monthly_usage(self): + """Return the month's usage.""" + return self._this_month + + def get_yearly_usage(self): + """Return the year's usage.""" + return self._this_year + + +class StreamLabsDailyUsage(Entity): + """Monitors the daily water usage.""" + + def __init__(self, location_name, streamlabs_usage_data): + """Initialize the daily water usage device.""" + self._location_name = location_name + self._streamlabs_usage_data = streamlabs_usage_data + self._state = None + + @property + def name(self): + """Return the name for daily usage.""" + return "{} {}".format(self._location_name, NAME_DAILY_USAGE) + + @property + def icon(self): + """Return the daily usage icon.""" + return WATER_ICON + + @property + def state(self): + """Return the current daily usage.""" + return self._streamlabs_usage_data.get_daily_usage() + + @property + def unit_of_measurement(self): + """Return gallons as the unit measurement for water.""" + return VOLUME_GALLONS + + def update(self): + """Retrieve the latest daily usage.""" + self._streamlabs_usage_data.update() + + +class StreamLabsMonthlyUsage(StreamLabsDailyUsage): + """Monitors the monthly water usage.""" + + @property + def name(self): + """Return the name for monthly usage.""" + return "{} {}".format(self._location_name, NAME_MONTHLY_USAGE) + + @property + def state(self): + """Return the current monthly usage.""" + return self._streamlabs_usage_data.get_monthly_usage() + + +class StreamLabsYearlyUsage(StreamLabsDailyUsage): + """Monitors the yearly water usage.""" + + @property + def name(self): + """Return the name for yearly usage.""" + return "{} {}".format(self._location_name, NAME_YEARLY_USAGE) + + @property + def state(self): + """Return the current yearly usage.""" + return self._streamlabs_usage_data.get_yearly_usage() diff --git a/homeassistant/components/streamlabswater/services.yaml b/homeassistant/components/streamlabswater/services.yaml new file mode 100644 index 00000000000..fa2a04c9586 --- /dev/null +++ b/homeassistant/components/streamlabswater/services.yaml @@ -0,0 +1,4 @@ +set_away_mode: + description: 'Set the home/away mode for a Streamlabs Water Monitor.' + fields: + away_mode: {description: home or away, example: 'home'} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index 45e64139ca1..6ef024957e7 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1707,6 +1707,9 @@ statsd==3.2.1 # homeassistant.components.steam_online steamodd==4.21 +# homeassistant.components.streamlabswater +streamlabswater==1.0.1 + # homeassistant.components.solaredge # homeassistant.components.thermoworks_smoke # homeassistant.components.traccar