mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
commit
27c6c27db6
@ -37,6 +37,7 @@ omit =
|
|||||||
homeassistant/components/device_tracker/asuswrt.py
|
homeassistant/components/device_tracker/asuswrt.py
|
||||||
homeassistant/components/device_tracker/ddwrt.py
|
homeassistant/components/device_tracker/ddwrt.py
|
||||||
homeassistant/components/device_tracker/luci.py
|
homeassistant/components/device_tracker/luci.py
|
||||||
|
homeassistant/components/device_tracker/ubus.py
|
||||||
homeassistant/components/device_tracker/netgear.py
|
homeassistant/components/device_tracker/netgear.py
|
||||||
homeassistant/components/device_tracker/nmap_tracker.py
|
homeassistant/components/device_tracker/nmap_tracker.py
|
||||||
homeassistant/components/device_tracker/thomson.py
|
homeassistant/components/device_tracker/thomson.py
|
||||||
@ -49,8 +50,10 @@ omit =
|
|||||||
homeassistant/components/light/hue.py
|
homeassistant/components/light/hue.py
|
||||||
homeassistant/components/light/limitlessled.py
|
homeassistant/components/light/limitlessled.py
|
||||||
homeassistant/components/light/blinksticklight.py
|
homeassistant/components/light/blinksticklight.py
|
||||||
|
homeassistant/components/light/hyperion.py
|
||||||
homeassistant/components/media_player/cast.py
|
homeassistant/components/media_player/cast.py
|
||||||
homeassistant/components/media_player/denon.py
|
homeassistant/components/media_player/denon.py
|
||||||
|
homeassistant/components/media_player/firetv.py
|
||||||
homeassistant/components/media_player/itunes.py
|
homeassistant/components/media_player/itunes.py
|
||||||
homeassistant/components/media_player/kodi.py
|
homeassistant/components/media_player/kodi.py
|
||||||
homeassistant/components/media_player/mpd.py
|
homeassistant/components/media_player/mpd.py
|
||||||
@ -70,6 +73,7 @@ omit =
|
|||||||
homeassistant/components/sensor/arest.py
|
homeassistant/components/sensor/arest.py
|
||||||
homeassistant/components/sensor/bitcoin.py
|
homeassistant/components/sensor/bitcoin.py
|
||||||
homeassistant/components/sensor/command_sensor.py
|
homeassistant/components/sensor/command_sensor.py
|
||||||
|
homeassistant/components/sensor/cpuspeed.py
|
||||||
homeassistant/components/sensor/dht.py
|
homeassistant/components/sensor/dht.py
|
||||||
homeassistant/components/sensor/efergy.py
|
homeassistant/components/sensor/efergy.py
|
||||||
homeassistant/components/sensor/forecast.py
|
homeassistant/components/sensor/forecast.py
|
||||||
@ -89,10 +93,12 @@ omit =
|
|||||||
homeassistant/components/switch/command_switch.py
|
homeassistant/components/switch/command_switch.py
|
||||||
homeassistant/components/switch/edimax.py
|
homeassistant/components/switch/edimax.py
|
||||||
homeassistant/components/switch/hikvisioncam.py
|
homeassistant/components/switch/hikvisioncam.py
|
||||||
|
homeassistant/components/switch/rest.py
|
||||||
homeassistant/components/switch/rpi_gpio.py
|
homeassistant/components/switch/rpi_gpio.py
|
||||||
homeassistant/components/switch/transmission.py
|
homeassistant/components/switch/transmission.py
|
||||||
homeassistant/components/switch/wemo.py
|
homeassistant/components/switch/wemo.py
|
||||||
homeassistant/components/thermostat/nest.py
|
homeassistant/components/thermostat/nest.py
|
||||||
|
homeassistant/components/thermostat/radiotherm.py
|
||||||
|
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
|
@ -20,8 +20,8 @@ After you finish adding support for your device:
|
|||||||
- Update the supported devices in the `README.md` file.
|
- Update the supported devices in the `README.md` file.
|
||||||
- Add any new dependencies to `requirements_all.txt`. There is no ordering right now, so just add it to the end.
|
- Add any new dependencies to `requirements_all.txt`. There is no ordering right now, so just add it to the end.
|
||||||
- Update the `.coveragerc` file.
|
- Update the `.coveragerc` file.
|
||||||
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). The documentation is handled in a separate [git repository](https://github.com/balloob/home-assistant.io).
|
- Provide some documentation for [home-assistant.io](https://home-assistant.io/). The documentation is handled in a separate [git repository](https://github.com/balloob/home-assistant.io). It's OK to add a docstring with configuration details to the file header.
|
||||||
- Make sure all your code passes Pylint and flake8 (PEP8 and some more) validation. To generate reports, run `pylint homeassistant > pylint.txt` and `flake8 homeassistant --exclude bower_components,external > flake8.txt`.
|
- Make sure all your code passes ``pylint`` and ``flake8`` (PEP8 and some more) validation. To check your repository, run `./script/lint`.
|
||||||
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
|
- Create a Pull Request against the [**dev**](https://github.com/balloob/home-assistant/tree/dev) branch of Home Assistant.
|
||||||
- Check for comments and suggestions on your Pull Request and keep an eye on the [Travis output](https://travis-ci.org/balloob/home-assistant/).
|
- Check for comments and suggestions on your Pull Request and keep an eye on the [Travis output](https://travis-ci.org/balloob/home-assistant/).
|
||||||
|
|
||||||
|
11
Dockerfile
11
Dockerfile
@ -10,11 +10,10 @@ RUN apt-get update && \
|
|||||||
apt-get install -y --no-install-recommends nmap net-tools && \
|
apt-get install -y --no-install-recommends nmap net-tools && \
|
||||||
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
# Open Z-Wave disabled because broken
|
RUN apt-get update && \
|
||||||
#RUN apt-get update && \
|
apt-get install -y cython3 libudev-dev && \
|
||||||
# apt-get install -y cython3 libudev-dev && \
|
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
||||||
# apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
pip3 install "cython<0.23" && \
|
||||||
# pip3 install cython && \
|
script/build_python_openzwave
|
||||||
# scripts/build_python_openzwave
|
|
||||||
|
|
||||||
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]
|
CMD [ "python", "-m", "homeassistant", "--config", "/config" ]
|
||||||
|
@ -19,7 +19,7 @@ Examples of devices it can interface it:
|
|||||||
* Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT
|
* Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT
|
||||||
*
|
*
|
||||||
* [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors
|
* [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors
|
||||||
* [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), and iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api))
|
* [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)), and Amazon Fire TV (by way of [python-firetv](https://github.com/happyleavesaoc/python-firetv))
|
||||||
* Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/)
|
* Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/)
|
||||||
* Interaction with [IFTTT](https://ifttt.com/)
|
* Interaction with [IFTTT](https://ifttt.com/)
|
||||||
* Integrate data from the [Bitcoin](https://bitcoin.org) network, meteorological data from [OpenWeatherMap](http://openweathermap.org/) and [Forecast.io](https://forecast.io/), [Transmission](http://www.transmissionbt.com/), or [SABnzbd](http://sabnzbd.org).
|
* Integrate data from the [Bitcoin](https://bitcoin.org) network, meteorological data from [OpenWeatherMap](http://openweathermap.org/) and [Forecast.io](https://forecast.io/), [Transmission](http://www.transmissionbt.com/), or [SABnzbd](http://sabnzbd.org).
|
||||||
|
@ -8,7 +8,7 @@ import os
|
|||||||
|
|
||||||
from homeassistant.components import verisure
|
from homeassistant.components import verisure
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID, SERVICE_ALARM_TRIGGER,
|
||||||
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
|
SERVICE_ALARM_DISARM, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_AWAY)
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
@ -29,6 +29,7 @@ SERVICE_TO_METHOD = {
|
|||||||
SERVICE_ALARM_DISARM: 'alarm_disarm',
|
SERVICE_ALARM_DISARM: 'alarm_disarm',
|
||||||
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
|
SERVICE_ALARM_ARM_HOME: 'alarm_arm_home',
|
||||||
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
|
SERVICE_ALARM_ARM_AWAY: 'alarm_arm_away',
|
||||||
|
SERVICE_ALARM_TRIGGER: 'alarm_trigger'
|
||||||
}
|
}
|
||||||
|
|
||||||
ATTR_CODE = 'code'
|
ATTR_CODE = 'code'
|
||||||
@ -53,9 +54,9 @@ def setup(hass, config):
|
|||||||
target_alarms = component.extract_from_service(service)
|
target_alarms = component.extract_from_service(service)
|
||||||
|
|
||||||
if ATTR_CODE not in service.data:
|
if ATTR_CODE not in service.data:
|
||||||
return
|
code = None
|
||||||
|
else:
|
||||||
code = service.data[ATTR_CODE]
|
code = service.data[ATTR_CODE]
|
||||||
|
|
||||||
method = SERVICE_TO_METHOD[service.service]
|
method = SERVICE_TO_METHOD[service.service]
|
||||||
|
|
||||||
@ -72,36 +73,50 @@ def setup(hass, config):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def alarm_disarm(hass, code, entity_id=None):
|
def alarm_disarm(hass, code=None, entity_id=None):
|
||||||
""" Send the alarm the command for disarm. """
|
""" Send the alarm the command for disarm. """
|
||||||
data = {ATTR_CODE: code}
|
data = {}
|
||||||
|
if code:
|
||||||
|
data[ATTR_CODE] = code
|
||||||
if entity_id:
|
if entity_id:
|
||||||
data[ATTR_ENTITY_ID] = entity_id
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
hass.services.call(DOMAIN, SERVICE_ALARM_DISARM, data)
|
hass.services.call(DOMAIN, SERVICE_ALARM_DISARM, data)
|
||||||
|
|
||||||
|
|
||||||
def alarm_arm_home(hass, code, entity_id=None):
|
def alarm_arm_home(hass, code=None, entity_id=None):
|
||||||
""" Send the alarm the command for arm home. """
|
""" Send the alarm the command for arm home. """
|
||||||
data = {ATTR_CODE: code}
|
data = {}
|
||||||
|
if code:
|
||||||
|
data[ATTR_CODE] = code
|
||||||
if entity_id:
|
if entity_id:
|
||||||
data[ATTR_ENTITY_ID] = entity_id
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_HOME, data)
|
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_HOME, data)
|
||||||
|
|
||||||
|
|
||||||
def alarm_arm_away(hass, code, entity_id=None):
|
def alarm_arm_away(hass, code=None, entity_id=None):
|
||||||
""" Send the alarm the command for arm away. """
|
""" Send the alarm the command for arm away. """
|
||||||
data = {ATTR_CODE: code}
|
data = {}
|
||||||
|
if code:
|
||||||
|
data[ATTR_CODE] = code
|
||||||
if entity_id:
|
if entity_id:
|
||||||
data[ATTR_ENTITY_ID] = entity_id
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_AWAY, data)
|
hass.services.call(DOMAIN, SERVICE_ALARM_ARM_AWAY, data)
|
||||||
|
|
||||||
|
|
||||||
|
def alarm_trigger(hass, code=None, entity_id=None):
|
||||||
|
""" Send the alarm the command for trigger. """
|
||||||
|
data = {}
|
||||||
|
if code:
|
||||||
|
data[ATTR_CODE] = code
|
||||||
|
if entity_id:
|
||||||
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
|
hass.services.call(DOMAIN, SERVICE_ALARM_TRIGGER, data)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=no-self-use
|
# pylint: disable=no-self-use
|
||||||
class AlarmControlPanel(Entity):
|
class AlarmControlPanel(Entity):
|
||||||
""" ABC for alarm control devices. """
|
""" ABC for alarm control devices. """
|
||||||
@ -123,6 +138,10 @@ class AlarmControlPanel(Entity):
|
|||||||
""" Send arm away command. """
|
""" Send arm away command. """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def alarm_trigger(self, code=None):
|
||||||
|
""" Send alarm trigger command. """
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
""" Return the state attributes. """
|
""" Return the state attributes. """
|
||||||
|
149
homeassistant/components/alarm_control_panel/manual.py
Normal file
149
homeassistant/components/alarm_control_panel/manual.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.alarm_control_panel.manual
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Support for manual alarms.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/alarm_control_panel.manual.html
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
import homeassistant.components.alarm_control_panel as alarm
|
||||||
|
from homeassistant.helpers.event import track_point_in_time
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
|
||||||
|
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEPENDENCIES = []
|
||||||
|
|
||||||
|
DEFAULT_ALARM_NAME = 'HA Alarm'
|
||||||
|
DEFAULT_PENDING_TIME = 60
|
||||||
|
DEFAULT_TRIGGER_TIME = 120
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up the manual alarm platform. """
|
||||||
|
|
||||||
|
add_devices([ManualAlarm(
|
||||||
|
hass,
|
||||||
|
config.get('name', DEFAULT_ALARM_NAME),
|
||||||
|
config.get('code'),
|
||||||
|
config.get('pending_time', DEFAULT_PENDING_TIME),
|
||||||
|
config.get('trigger_time', DEFAULT_TRIGGER_TIME),
|
||||||
|
)])
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
class ManualAlarm(alarm.AlarmControlPanel):
|
||||||
|
"""
|
||||||
|
Represents an alarm status.
|
||||||
|
|
||||||
|
When armed, will be pending for 'pending_time', after that armed.
|
||||||
|
When triggered, will be pending for 'trigger_time'. After that will be
|
||||||
|
triggered for 'trigger_time', after that we return to disarmed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hass, name, code, pending_time, trigger_time):
|
||||||
|
self._state = STATE_ALARM_DISARMED
|
||||||
|
self._hass = hass
|
||||||
|
self._name = name
|
||||||
|
self._code = str(code) if code else None
|
||||||
|
self._pending_time = datetime.timedelta(seconds=pending_time)
|
||||||
|
self._trigger_time = datetime.timedelta(seconds=trigger_time)
|
||||||
|
self._state_ts = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
""" No polling needed. """
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the device. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the state of the device. """
|
||||||
|
if self._state in (STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY) and \
|
||||||
|
self._pending_time and self._state_ts + self._pending_time > \
|
||||||
|
dt_util.utcnow():
|
||||||
|
return STATE_ALARM_PENDING
|
||||||
|
|
||||||
|
if self._state == STATE_ALARM_TRIGGERED and self._trigger_time:
|
||||||
|
if self._state_ts + self._pending_time > dt_util.utcnow():
|
||||||
|
return STATE_ALARM_PENDING
|
||||||
|
elif (self._state_ts + self._pending_time +
|
||||||
|
self._trigger_time) < dt_util.utcnow():
|
||||||
|
return STATE_ALARM_DISARMED
|
||||||
|
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def code_format(self):
|
||||||
|
""" One or more characters. """
|
||||||
|
return None if self._code is None else '.+'
|
||||||
|
|
||||||
|
def alarm_disarm(self, code=None):
|
||||||
|
""" Send disarm command. """
|
||||||
|
if not self._validate_code(code, STATE_ALARM_DISARMED):
|
||||||
|
return
|
||||||
|
|
||||||
|
self._state = STATE_ALARM_DISARMED
|
||||||
|
self._state_ts = dt_util.utcnow()
|
||||||
|
self.update_ha_state()
|
||||||
|
|
||||||
|
def alarm_arm_home(self, code=None):
|
||||||
|
""" Send arm home command. """
|
||||||
|
if not self._validate_code(code, STATE_ALARM_ARMED_HOME):
|
||||||
|
return
|
||||||
|
|
||||||
|
self._state = STATE_ALARM_ARMED_HOME
|
||||||
|
self._state_ts = dt_util.utcnow()
|
||||||
|
self.update_ha_state()
|
||||||
|
|
||||||
|
if self._pending_time:
|
||||||
|
track_point_in_time(
|
||||||
|
self._hass, self.update_ha_state,
|
||||||
|
self._state_ts + self._pending_time)
|
||||||
|
|
||||||
|
def alarm_arm_away(self, code=None):
|
||||||
|
""" Send arm away command. """
|
||||||
|
if not self._validate_code(code, STATE_ALARM_ARMED_AWAY):
|
||||||
|
return
|
||||||
|
|
||||||
|
self._state = STATE_ALARM_ARMED_AWAY
|
||||||
|
self._state_ts = dt_util.utcnow()
|
||||||
|
self.update_ha_state()
|
||||||
|
|
||||||
|
if self._pending_time:
|
||||||
|
track_point_in_time(
|
||||||
|
self._hass, self.update_ha_state,
|
||||||
|
self._state_ts + self._pending_time)
|
||||||
|
|
||||||
|
def alarm_trigger(self, code=None):
|
||||||
|
""" Send alarm trigger command. No code needed. """
|
||||||
|
self._state = STATE_ALARM_TRIGGERED
|
||||||
|
self._state_ts = dt_util.utcnow()
|
||||||
|
self.update_ha_state()
|
||||||
|
|
||||||
|
if self._trigger_time:
|
||||||
|
track_point_in_time(
|
||||||
|
self._hass, self.update_ha_state,
|
||||||
|
self._state_ts + self._pending_time)
|
||||||
|
|
||||||
|
track_point_in_time(
|
||||||
|
self._hass, self.update_ha_state,
|
||||||
|
self._state_ts + self._pending_time + self._trigger_time)
|
||||||
|
|
||||||
|
def _validate_code(self, code, state):
|
||||||
|
""" Validate given code. """
|
||||||
|
check = self._code is None or code == self._code
|
||||||
|
if not check:
|
||||||
|
_LOGGER.warning('Invalid code given for %s', state)
|
||||||
|
return check
|
@ -1,68 +1,18 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.alarm_control_panel.mqtt
|
homeassistant.components.alarm_control_panel.mqtt
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This platform enables the possibility to control a MQTT alarm.
|
This platform enables the possibility to control a MQTT alarm.
|
||||||
In this platform, 'state_topic' and 'command_topic' are required.
|
|
||||||
The alarm will only change state after receiving the a new state
|
|
||||||
from 'state_topic'. If these messages are published with RETAIN flag,
|
|
||||||
the MQTT alarm will receive an instant state update after subscription
|
|
||||||
and will start with correct state. Otherwise, the initial state will
|
|
||||||
be 'unknown'.
|
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/alarm_control_panel.mqtt.html
|
||||||
alarm_control_panel:
|
|
||||||
platform: mqtt
|
|
||||||
name: "MQTT Alarm"
|
|
||||||
state_topic: "home/alarm"
|
|
||||||
command_topic: "home/alarm/set"
|
|
||||||
qos: 0
|
|
||||||
payload_disarm: "DISARM"
|
|
||||||
payload_arm_home: "ARM_HOME"
|
|
||||||
payload_arm_away: "ARM_AWAY"
|
|
||||||
code: "mySecretCode"
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
The name of the alarm. Default is 'MQTT Alarm'.
|
|
||||||
|
|
||||||
state_topic
|
|
||||||
*Required
|
|
||||||
The MQTT topic subscribed to receive state updates.
|
|
||||||
|
|
||||||
command_topic
|
|
||||||
*Required
|
|
||||||
The MQTT topic to publish commands to change the alarm state.
|
|
||||||
|
|
||||||
qos
|
|
||||||
*Optional
|
|
||||||
The maximum QoS level of the state topic. Default is 0.
|
|
||||||
This QoS will also be used to publishing messages.
|
|
||||||
|
|
||||||
payload_disarm
|
|
||||||
*Optional
|
|
||||||
The payload do disarm alarm. Default is "DISARM".
|
|
||||||
|
|
||||||
payload_arm_home
|
|
||||||
*Optional
|
|
||||||
The payload to set armed-home mode. Default is "ARM_HOME".
|
|
||||||
|
|
||||||
payload_arm_away
|
|
||||||
*Optional
|
|
||||||
The payload to set armed-away mode. Default is "ARM_AWAY".
|
|
||||||
|
|
||||||
code
|
|
||||||
*Optional
|
|
||||||
If defined, specifies a code to enable or disable the alarm in the frontend.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import homeassistant.components.mqtt as mqtt
|
import homeassistant.components.mqtt as mqtt
|
||||||
import homeassistant.components.alarm_control_panel as alarm
|
import homeassistant.components.alarm_control_panel as alarm
|
||||||
|
|
||||||
from homeassistant.const import (STATE_UNKNOWN)
|
from homeassistant.const import (
|
||||||
|
STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY,
|
||||||
|
STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -99,6 +49,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||||
|
# pylint: disable=abstract-method
|
||||||
class MqttAlarm(alarm.AlarmControlPanel):
|
class MqttAlarm(alarm.AlarmControlPanel):
|
||||||
""" represents a MQTT alarm status within home assistant. """
|
""" represents a MQTT alarm status within home assistant. """
|
||||||
|
|
||||||
@ -113,10 +64,15 @@ class MqttAlarm(alarm.AlarmControlPanel):
|
|||||||
self._payload_disarm = payload_disarm
|
self._payload_disarm = payload_disarm
|
||||||
self._payload_arm_home = payload_arm_home
|
self._payload_arm_home = payload_arm_home
|
||||||
self._payload_arm_away = payload_arm_away
|
self._payload_arm_away = payload_arm_away
|
||||||
self._code = code
|
self._code = str(code) if code else None
|
||||||
|
|
||||||
def message_received(topic, payload, qos):
|
def message_received(topic, payload, qos):
|
||||||
""" A new MQTT message has been received. """
|
""" A new MQTT message has been received. """
|
||||||
|
if payload not in (STATE_ALARM_DISARMED, STATE_ALARM_ARMED_HOME,
|
||||||
|
STATE_ALARM_ARMED_AWAY, STATE_ALARM_PENDING,
|
||||||
|
STATE_ALARM_TRIGGERED):
|
||||||
|
_LOGGER.warning('Received unexpected payload: %s', payload)
|
||||||
|
return
|
||||||
self._state = payload
|
self._state = payload
|
||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
|
|
||||||
@ -144,24 +100,28 @@ class MqttAlarm(alarm.AlarmControlPanel):
|
|||||||
|
|
||||||
def alarm_disarm(self, code=None):
|
def alarm_disarm(self, code=None):
|
||||||
""" Send disarm command. """
|
""" Send disarm command. """
|
||||||
if code == str(self._code) or self.code_format is None:
|
if not self._validate_code(code, 'disarming'):
|
||||||
mqtt.publish(self.hass, self._command_topic,
|
return
|
||||||
self._payload_disarm, self._qos)
|
mqtt.publish(self.hass, self._command_topic,
|
||||||
else:
|
self._payload_disarm, self._qos)
|
||||||
_LOGGER.warning("Wrong code entered while disarming!")
|
|
||||||
|
|
||||||
def alarm_arm_home(self, code=None):
|
def alarm_arm_home(self, code=None):
|
||||||
""" Send arm home command. """
|
""" Send arm home command. """
|
||||||
if code == str(self._code) or self.code_format is None:
|
if not self._validate_code(code, 'arming home'):
|
||||||
mqtt.publish(self.hass, self._command_topic,
|
return
|
||||||
self._payload_arm_home, self._qos)
|
mqtt.publish(self.hass, self._command_topic,
|
||||||
else:
|
self._payload_arm_home, self._qos)
|
||||||
_LOGGER.warning("Wrong code entered while arming home!")
|
|
||||||
|
|
||||||
def alarm_arm_away(self, code=None):
|
def alarm_arm_away(self, code=None):
|
||||||
""" Send arm away command. """
|
""" Send arm away command. """
|
||||||
if code == str(self._code) or self.code_format is None:
|
if not self._validate_code(code, 'arming away'):
|
||||||
mqtt.publish(self.hass, self._command_topic,
|
return
|
||||||
self._payload_arm_away, self._qos)
|
mqtt.publish(self.hass, self._command_topic,
|
||||||
else:
|
self._payload_arm_away, self._qos)
|
||||||
_LOGGER.warning("Wrong code entered while arming away!")
|
|
||||||
|
def _validate_code(self, code, state):
|
||||||
|
""" Validate given code. """
|
||||||
|
check = self._code is None or code == self._code
|
||||||
|
if not check:
|
||||||
|
_LOGGER.warning('Wrong code entered for %s', state)
|
||||||
|
return check
|
||||||
|
@ -33,6 +33,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
add_devices(alarms)
|
add_devices(alarms)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=abstract-method
|
||||||
class VerisureAlarm(alarm.AlarmControlPanel):
|
class VerisureAlarm(alarm.AlarmControlPanel):
|
||||||
""" Represents a Verisure alarm status. """
|
""" Represents a Verisure alarm status. """
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.api
|
homeassistant.components.api
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides a Rest API for Home Assistant.
|
Provides a Rest API for Home Assistant.
|
||||||
|
|
||||||
|
For more details about the RESTful API, please refer to the documentation at
|
||||||
|
https://home-assistant.io/developers/api.html
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
@ -4,26 +4,8 @@ components.arduino
|
|||||||
Arduino component that connects to a directly attached Arduino board which
|
Arduino component that connects to a directly attached Arduino board which
|
||||||
runs with the Firmata firmware.
|
runs with the Firmata firmware.
|
||||||
|
|
||||||
Configuration:
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/arduino.html
|
||||||
To use the Arduino board you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
arduino:
|
|
||||||
port: /dev/ttyACM0
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
port
|
|
||||||
*Required
|
|
||||||
The port where is your board connected to your Home Assistant system.
|
|
||||||
If you are using an original Arduino the port will be named ttyACM*. The exact
|
|
||||||
number can be determined with 'ls /dev/ttyACM*' or check your 'dmesg'/
|
|
||||||
'journalctl -f' output. Keep in mind that Arduino clones are often using a
|
|
||||||
different name for the port (e.g. '/dev/ttyUSB*').
|
|
||||||
|
|
||||||
A word of caution: The Arduino is not storing states. This means that with
|
|
||||||
every initialization the pins are set to off/low.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.event
|
homeassistant.components.automation.event
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Offers event listening automation rules.
|
Offers event listening automation rules.
|
||||||
|
|
||||||
|
For more details about this automation rule, please refer to the documentation
|
||||||
|
at https://home-assistant.io/components/automation.html#event-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.mqtt
|
homeassistant.components.automation.mqtt
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Offers MQTT listening automation rules.
|
Offers MQTT listening automation rules.
|
||||||
|
|
||||||
|
For more details about this automation rule, please refer to the documentation
|
||||||
|
at https://home-assistant.io/components/automation.html#mqtt-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.numeric_state
|
homeassistant.components.automation.numeric_state
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Offers numeric state listening automation rules.
|
Offers numeric state listening automation rules.
|
||||||
|
|
||||||
|
For more details about this automation rule, please refer to the documentation
|
||||||
|
at https://home-assistant.io/components/automation.html#numeric-state-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.state
|
homeassistant.components.automation.state
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Offers state listening automation rules.
|
Offers state listening automation rules.
|
||||||
|
|
||||||
|
For more details about this automation rule, please refer to the documentation
|
||||||
|
at https://home-assistant.io/components/automation.html#state-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -28,6 +30,11 @@ def trigger(hass, config, action):
|
|||||||
from_state = config.get(CONF_FROM, MATCH_ALL)
|
from_state = config.get(CONF_FROM, MATCH_ALL)
|
||||||
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
|
to_state = config.get(CONF_TO) or config.get(CONF_STATE) or MATCH_ALL
|
||||||
|
|
||||||
|
if isinstance(from_state, bool) or isinstance(to_state, bool):
|
||||||
|
logging.getLogger(__name__).error(
|
||||||
|
'Config error. Surround to/from values with quotes.')
|
||||||
|
return False
|
||||||
|
|
||||||
def state_automation_listener(entity, from_s, to_s):
|
def state_automation_listener(entity, from_s, to_s):
|
||||||
""" Listens for state changes and calls action. """
|
""" Listens for state changes and calls action. """
|
||||||
action()
|
action()
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.sun
|
homeassistant.components.automation.sun
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Offers sun based automation rules.
|
Offers sun based automation rules.
|
||||||
|
|
||||||
|
For more details about this automation rule, please refer to the documentation
|
||||||
|
at https://home-assistant.io/components/automation.html#sun-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.time
|
homeassistant.components.automation.time
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Offers time listening automation rules.
|
Offers time listening automation rules.
|
||||||
|
|
||||||
|
For more details about this automation rule, please refer to the documentation
|
||||||
|
at https://home-assistant.io/components/automation.html#time-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.automation.zone
|
homeassistant.components.automation.zone
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Offers zone automation rules.
|
Offers zone automation rules.
|
||||||
|
|
||||||
|
For more details about this automation rule, please refer to the documentation
|
||||||
|
at https://home-assistant.io/components/automation.html#zone-trigger
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -3,42 +3,6 @@ homeassistant.components.camera.foscam
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
This component provides basic support for Foscam IP cameras.
|
This component provides basic support for Foscam IP cameras.
|
||||||
|
|
||||||
As part of the basic support the following features will be provided:
|
|
||||||
-MJPEG video streaming
|
|
||||||
|
|
||||||
To use this component, add the following to your configuration.yaml file.
|
|
||||||
|
|
||||||
camera:
|
|
||||||
platform: foscam
|
|
||||||
name: Door Camera
|
|
||||||
ip: 192.168.0.123
|
|
||||||
port: 88
|
|
||||||
username: YOUR_USERNAME
|
|
||||||
password: YOUR_PASSWORD
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
ip
|
|
||||||
*Required
|
|
||||||
The IP address of your Foscam device.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of a visitor or operator of your camera. Oddly admin accounts
|
|
||||||
don't seem to have access to take snapshots.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for accessing your camera.
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
This parameter allows you to override the name of your camera in homeassistant.
|
|
||||||
|
|
||||||
port
|
|
||||||
*Optional
|
|
||||||
The port that the camera is running on. The default is 88.
|
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/camera.foscam.html
|
https://home-assistant.io/components/camera.foscam.html
|
||||||
"""
|
"""
|
||||||
|
@ -3,43 +3,8 @@ homeassistant.components.camera.generic
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for IP Cameras.
|
Support for IP Cameras.
|
||||||
|
|
||||||
This component provides basic support for IP cameras. For the basic support to
|
For more details about this platform, please refer to the documentation at
|
||||||
work you camera must support accessing a JPEG snapshot via a URL and you will
|
https://home-assistant.io/components/camera.generic.html
|
||||||
need to specify the "still_image_url" parameter which should be the location of
|
|
||||||
the JPEG image.
|
|
||||||
|
|
||||||
As part of the basic support the following features will be provided:
|
|
||||||
- MJPEG video streaming
|
|
||||||
- Saving a snapshot
|
|
||||||
- Recording(JPEG frame capture)
|
|
||||||
|
|
||||||
To use this component, add the following to your configuration.yaml file.
|
|
||||||
|
|
||||||
camera:
|
|
||||||
platform: generic
|
|
||||||
name: Door Camera
|
|
||||||
username: YOUR_USERNAME
|
|
||||||
password: YOUR_PASSWORD
|
|
||||||
still_image_url: http://YOUR_CAMERA_IP_AND_PORT/image.jpg
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
still_image_url
|
|
||||||
*Required
|
|
||||||
The URL your camera serves the image on, eg. http://192.168.1.21:2112/
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
This parameter allows you to override the name of your camera in Home
|
|
||||||
Assistant.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Optional
|
|
||||||
The username for accessing your camera.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Optional
|
|
||||||
The password for accessing your camera.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.conversation
|
homeassistant.components.conversation
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides functionality to have conversations with Home Assistant.
|
Provides functionality to have conversations with Home Assistant.
|
||||||
This is more a proof of concept.
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/conversation.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
@ -63,6 +63,14 @@ def setup(hass, config):
|
|||||||
'still_image_url': 'http://home-assistant.io/demo/webcam.jpg',
|
'still_image_url': 'http://home-assistant.io/demo/webcam.jpg',
|
||||||
}})
|
}})
|
||||||
|
|
||||||
|
# Setup alarm_control_panel
|
||||||
|
bootstrap.setup_component(
|
||||||
|
hass, 'alarm_control_panel',
|
||||||
|
{'alarm_control_panel': {
|
||||||
|
'platform': 'manual',
|
||||||
|
'name': 'Test Alarm',
|
||||||
|
}})
|
||||||
|
|
||||||
# Setup scripts
|
# Setup scripts
|
||||||
bootstrap.setup_component(
|
bootstrap.setup_component(
|
||||||
hass, 'script',
|
hass, 'script',
|
||||||
|
@ -4,41 +4,8 @@ homeassistant.components.device_tracker.actiontec
|
|||||||
Device tracker platform that supports scanning an Actiontec MI424WR
|
Device tracker platform that supports scanning an Actiontec MI424WR
|
||||||
(Verizon FIOS) router for device presence.
|
(Verizon FIOS) router for device presence.
|
||||||
|
|
||||||
This device tracker needs telnet to be enabled on the router.
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.actiontec.html
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the Actiontec tracker you will need to add something like the
|
|
||||||
following to your configuration.yaml file. If you experience disconnects
|
|
||||||
you can modify the home_interval variable.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: actiontec
|
|
||||||
host: YOUR_ROUTER_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
# optional:
|
|
||||||
home_interval: 10
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
|
|
||||||
home_interval
|
|
||||||
*Optional
|
|
||||||
If the home_interval is set then the component will not let a device
|
|
||||||
be AWAY if it has been HOME in the last home_interval minutes. This is
|
|
||||||
in addition to the 3 minute wait built into the device_tracker component.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
@ -56,7 +23,7 @@ from homeassistant.components.device_tracker import DOMAIN
|
|||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
|
|
||||||
# interval in minutes to exclude devices from a scan while they are home
|
# Interval in minutes to exclude devices from a scan while they are home
|
||||||
CONF_HOME_INTERVAL = "home_interval"
|
CONF_HOME_INTERVAL = "home_interval"
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -4,33 +4,8 @@ homeassistant.components.device_tracker.aruba
|
|||||||
Device tracker platform that supports scanning a Aruba Access Point for device
|
Device tracker platform that supports scanning a Aruba Access Point for device
|
||||||
presence.
|
presence.
|
||||||
|
|
||||||
This device tracker needs telnet to be enabled on the router.
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.aruba.html
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the Aruba tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file. You also need to enable Telnet in the
|
|
||||||
configuration page of your router.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: aruba
|
|
||||||
host: YOUR_ACCESS_POINT_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -161,9 +161,10 @@ class AsusWrtDeviceScanner(object):
|
|||||||
# For leases where the client doesn't set a hostname, ensure
|
# For leases where the client doesn't set a hostname, ensure
|
||||||
# it is blank and not '*', which breaks the entity_id down
|
# it is blank and not '*', which breaks the entity_id down
|
||||||
# the line
|
# the line
|
||||||
host = match.group('host')
|
if match:
|
||||||
if host == '*':
|
host = match.group('host')
|
||||||
host = ''
|
if host == '*':
|
||||||
|
host = ''
|
||||||
|
|
||||||
devices[match.group('ip')] = {
|
devices[match.group('ip')] = {
|
||||||
'host': host,
|
'host': host,
|
||||||
@ -174,6 +175,6 @@ class AsusWrtDeviceScanner(object):
|
|||||||
|
|
||||||
for neighbor in neighbors:
|
for neighbor in neighbors:
|
||||||
match = _IP_NEIGH_REGEX.search(neighbor.decode('utf-8'))
|
match = _IP_NEIGH_REGEX.search(neighbor.decode('utf-8'))
|
||||||
if match.group('ip') in devices:
|
if match and match.group('ip') in devices:
|
||||||
devices[match.group('ip')]['status'] = match.group('status')
|
devices[match.group('ip')]['status'] = match.group('status')
|
||||||
return devices
|
return devices
|
||||||
|
@ -4,30 +4,8 @@ homeassistant.components.device_tracker.ddwrt
|
|||||||
Device tracker platform that supports scanning a DD-WRT router for device
|
Device tracker platform that supports scanning a DD-WRT router for device
|
||||||
presence.
|
presence.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.ddwrt.html
|
||||||
To use the DD-WRT tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: ddwrt
|
|
||||||
host: YOUR_ROUTER_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
71
homeassistant/components/device_tracker/geofancy.py
Normal file
71
homeassistant/components/device_tracker/geofancy.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.device_tracker.geofancy
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Geofancy platform for the device tracker.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.geofancy.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
HTTP_UNPROCESSABLE_ENTITY, HTTP_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
DEPENDENCIES = ['http']
|
||||||
|
|
||||||
|
_SEE = 0
|
||||||
|
|
||||||
|
URL_API_GEOFANCY_ENDPOINT = "/api/geofancy"
|
||||||
|
|
||||||
|
|
||||||
|
def setup_scanner(hass, config, see):
|
||||||
|
""" Set up an endpoint for the Geofancy app. """
|
||||||
|
|
||||||
|
# Use a global variable to keep setup_scanner compact when using a callback
|
||||||
|
global _SEE
|
||||||
|
_SEE = see
|
||||||
|
|
||||||
|
# POST would be semantically better, but that currently does not work
|
||||||
|
# since Geofancy sends the data as key1=value1&key2=value2
|
||||||
|
# in the request body, while Home Assistant expects json there.
|
||||||
|
|
||||||
|
hass.http.register_path(
|
||||||
|
'GET', URL_API_GEOFANCY_ENDPOINT, _handle_get_api_geofancy)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_get_api_geofancy(handler, path_match, data):
|
||||||
|
""" Geofancy message received. """
|
||||||
|
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
handler.write_json_message(
|
||||||
|
"Error while parsing Geofancy message.",
|
||||||
|
HTTP_INTERNAL_SERVER_ERROR)
|
||||||
|
return
|
||||||
|
if 'latitude' not in data or 'longitude' not in data:
|
||||||
|
handler.write_json_message(
|
||||||
|
"Location not specified.",
|
||||||
|
HTTP_UNPROCESSABLE_ENTITY)
|
||||||
|
return
|
||||||
|
if 'device' not in data or 'id' not in data:
|
||||||
|
handler.write_json_message(
|
||||||
|
"Device id or location id not specified.",
|
||||||
|
HTTP_UNPROCESSABLE_ENTITY)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
gps_coords = (float(data['latitude']), float(data['longitude']))
|
||||||
|
except ValueError:
|
||||||
|
# If invalid latitude / longitude format
|
||||||
|
handler.write_json_message(
|
||||||
|
"Invalid latitude / longitude format.",
|
||||||
|
HTTP_UNPROCESSABLE_ENTITY)
|
||||||
|
return
|
||||||
|
|
||||||
|
# entity id's in Home Assistant must be alphanumerical
|
||||||
|
device_uuid = data['device']
|
||||||
|
device_entity_id = device_uuid.replace('-', '')
|
||||||
|
|
||||||
|
_SEE(dev_id=device_entity_id, gps=gps_coords, location_name=data['id'])
|
||||||
|
|
||||||
|
handler.write_json_message("Geofancy message processed")
|
@ -4,33 +4,8 @@ homeassistant.components.device_tracker.luci
|
|||||||
Device tracker platform that supports scanning a OpenWRT router for device
|
Device tracker platform that supports scanning a OpenWRT router for device
|
||||||
presence.
|
presence.
|
||||||
|
|
||||||
It's required that the luci RPC package is installed on the OpenWRT router:
|
For more details about this platform, please refer to the documentation at
|
||||||
# opkg install luci-mod-rpc
|
https://home-assistant.io/components/device_tracker.luci.html
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the Luci tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: luci
|
|
||||||
host: YOUR_ROUTER_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.mqtt
|
homeassistant.components.device_tracker.mqtt
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
MQTT platform for the device tracker.
|
MQTT platform for the device tracker.
|
||||||
|
|
||||||
device_tracker:
|
For more details about this platform, please refer to the documentation at
|
||||||
platform: mqtt
|
https://home-assistant.io/components/device_tracker.mqtt.html
|
||||||
qos: 1
|
|
||||||
devices:
|
|
||||||
paulus_oneplus: /location/paulus
|
|
||||||
annetherese_n4: /location/annetherese
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from homeassistant import util
|
from homeassistant import util
|
||||||
|
@ -4,30 +4,8 @@ homeassistant.components.device_tracker.netgear
|
|||||||
Device tracker platform that supports scanning a Netgear router for device
|
Device tracker platform that supports scanning a Netgear router for device
|
||||||
presence.
|
presence.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.netgear.html
|
||||||
To use the Netgear tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: netgear
|
|
||||||
host: YOUR_ROUTER_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -3,26 +3,8 @@ homeassistant.components.device_tracker.nmap
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Device tracker platform that supports scanning a network with nmap.
|
Device tracker platform that supports scanning a network with nmap.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.nmap_scanner.html
|
||||||
To use the nmap tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: nmap_tracker
|
|
||||||
hosts: 192.168.1.1/24
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
hosts
|
|
||||||
*Required
|
|
||||||
The IP addresses to scan in the network-prefix notation (192.168.1.1/24) or
|
|
||||||
the range notation (192.168.1.1-255).
|
|
||||||
|
|
||||||
home_interval
|
|
||||||
*Optional
|
|
||||||
Number of minutes it will not scan devices that it found in previous results.
|
|
||||||
This is to save battery.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.device_tracker.owntracks
|
homeassistant.components.device_tracker.owntracks
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
OwnTracks platform for the device tracker.
|
OwnTracks platform for the device tracker.
|
||||||
|
|
||||||
device_tracker:
|
For more details about this platform, please refer to the documentation at
|
||||||
platform: owntracks
|
https://home-assistant.io/components/device_tracker.owntracks.html
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
@ -4,32 +4,8 @@ homeassistant.components.device_tracker.thomson
|
|||||||
Device tracker platform that supports scanning a THOMSON router for device
|
Device tracker platform that supports scanning a THOMSON router for device
|
||||||
presence.
|
presence.
|
||||||
|
|
||||||
This device tracker needs telnet to be enabled on the router.
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.thomson.html
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the THOMSON tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: thomson
|
|
||||||
host: YOUR_ROUTER_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -4,36 +4,8 @@ homeassistant.components.device_tracker.tomato
|
|||||||
Device tracker platform that supports scanning a Tomato router for device
|
Device tracker platform that supports scanning a Tomato router for device
|
||||||
presence.
|
presence.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.tomato.html
|
||||||
To use the Tomato tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: tomato
|
|
||||||
host: YOUR_ROUTER_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
http_id: ABCDEFG
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
|
|
||||||
http_id
|
|
||||||
*Required
|
|
||||||
The value can be obtained by logging in to the Tomato admin interface and
|
|
||||||
search for http_id in the page source code.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
@ -4,30 +4,8 @@ homeassistant.components.device_tracker.tplink
|
|||||||
Device tracker platform that supports scanning a TP-Link router for device
|
Device tracker platform that supports scanning a TP-Link router for device
|
||||||
presence.
|
presence.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.tplink.html
|
||||||
To use the TP-Link tracker you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
device_tracker:
|
|
||||||
platform: tplink
|
|
||||||
host: YOUR_ROUTER_IP
|
|
||||||
username: YOUR_ADMIN_USERNAME
|
|
||||||
password: YOUR_ADMIN_PASSWORD
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your router, e.g. 192.168.1.1.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
The username of an user with administrative privileges, usually 'admin'.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given admin account.
|
|
||||||
"""
|
"""
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
@ -54,10 +32,13 @@ def get_scanner(hass, config):
|
|||||||
_LOGGER):
|
_LOGGER):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
scanner = Tplink2DeviceScanner(config[DOMAIN])
|
scanner = Tplink3DeviceScanner(config[DOMAIN])
|
||||||
|
|
||||||
if not scanner.success_init:
|
if not scanner.success_init:
|
||||||
scanner = TplinkDeviceScanner(config[DOMAIN])
|
scanner = Tplink2DeviceScanner(config[DOMAIN])
|
||||||
|
|
||||||
|
if not scanner.success_init:
|
||||||
|
scanner = TplinkDeviceScanner(config[DOMAIN])
|
||||||
|
|
||||||
return scanner if scanner.success_init else None
|
return scanner if scanner.success_init else None
|
||||||
|
|
||||||
@ -156,7 +137,7 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
|
|||||||
with self.lock:
|
with self.lock:
|
||||||
_LOGGER.info("Loading wireless clients...")
|
_LOGGER.info("Loading wireless clients...")
|
||||||
|
|
||||||
url = 'http://{}/data/map_access_wireless_client_grid.json'\
|
url = 'http://{}/data/map_access_wireless_client_grid.json' \
|
||||||
.format(self.host)
|
.format(self.host)
|
||||||
referer = 'http://{}'.format(self.host)
|
referer = 'http://{}'.format(self.host)
|
||||||
|
|
||||||
@ -166,7 +147,7 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
|
|||||||
b64_encoded_username_password = base64.b64encode(
|
b64_encoded_username_password = base64.b64encode(
|
||||||
username_password.encode('ascii')
|
username_password.encode('ascii')
|
||||||
).decode('ascii')
|
).decode('ascii')
|
||||||
cookie = 'Authorization=Basic {}'\
|
cookie = 'Authorization=Basic {}' \
|
||||||
.format(b64_encoded_username_password)
|
.format(b64_encoded_username_password)
|
||||||
|
|
||||||
response = requests.post(url, headers={'referer': referer,
|
response = requests.post(url, headers={'referer': referer,
|
||||||
@ -183,7 +164,119 @@ class Tplink2DeviceScanner(TplinkDeviceScanner):
|
|||||||
self.last_results = {
|
self.last_results = {
|
||||||
device['mac_addr'].replace('-', ':'): device['name']
|
device['mac_addr'].replace('-', ':'): device['name']
|
||||||
for device in result
|
for device in result
|
||||||
}
|
}
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Tplink3DeviceScanner(TplinkDeviceScanner):
|
||||||
|
"""
|
||||||
|
This class queries the Archer C9 router running version 150811 or higher
|
||||||
|
of TP-Link firmware for connected devices.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
self.stok = ''
|
||||||
|
self.sysauth = ''
|
||||||
|
super(Tplink3DeviceScanner, self).__init__(config)
|
||||||
|
|
||||||
|
def scan_devices(self):
|
||||||
|
"""
|
||||||
|
Scans for new devices and return a list containing found device ids.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._update_info()
|
||||||
|
return self.last_results.keys()
|
||||||
|
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
def get_device_name(self, device):
|
||||||
|
"""
|
||||||
|
The TP-Link firmware doesn't save the name of the wireless device.
|
||||||
|
We are forced to use the MAC address as name here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.last_results.get(device)
|
||||||
|
|
||||||
|
def _get_auth_tokens(self):
|
||||||
|
"""
|
||||||
|
Retrieves auth tokens from the router.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_LOGGER.info("Retrieving auth tokens...")
|
||||||
|
|
||||||
|
url = 'http://{}/cgi-bin/luci/;stok=/login?form=login' \
|
||||||
|
.format(self.host)
|
||||||
|
referer = 'http://{}/webpages/login.html'.format(self.host)
|
||||||
|
|
||||||
|
# if possible implement rsa encryption of password here
|
||||||
|
|
||||||
|
response = requests.post(url,
|
||||||
|
params={'operation': 'login',
|
||||||
|
'username': self.username,
|
||||||
|
'password': self.password},
|
||||||
|
headers={'referer': referer})
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.stok = response.json().get('data').get('stok')
|
||||||
|
_LOGGER.info(self.stok)
|
||||||
|
regex_result = re.search('sysauth=(.*);',
|
||||||
|
response.headers['set-cookie'])
|
||||||
|
self.sysauth = regex_result.group(1)
|
||||||
|
_LOGGER.info(self.sysauth)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.error("Couldn't fetch auth tokens!")
|
||||||
|
return False
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
|
def _update_info(self):
|
||||||
|
"""
|
||||||
|
Ensures the information from the TP-Link router is up to date.
|
||||||
|
Returns boolean if scanning successful.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
if (self.stok == '') or (self.sysauth == ''):
|
||||||
|
self._get_auth_tokens()
|
||||||
|
|
||||||
|
_LOGGER.info("Loading wireless clients...")
|
||||||
|
|
||||||
|
url = 'http://{}/cgi-bin/luci/;stok={}/admin/wireless?form=statistics' \
|
||||||
|
.format(self.host, self.stok)
|
||||||
|
referer = 'http://{}/webpages/index.html'.format(self.host)
|
||||||
|
|
||||||
|
response = requests.post(url,
|
||||||
|
params={'operation': 'load'},
|
||||||
|
headers={'referer': referer},
|
||||||
|
cookies={'sysauth': self.sysauth})
|
||||||
|
|
||||||
|
try:
|
||||||
|
json_response = response.json()
|
||||||
|
|
||||||
|
if json_response.get('success'):
|
||||||
|
result = response.json().get('data')
|
||||||
|
else:
|
||||||
|
if json_response.get('errorcode') == 'timeout':
|
||||||
|
_LOGGER.info("Token timed out. "
|
||||||
|
"Relogging on next scan.")
|
||||||
|
self.stok = ''
|
||||||
|
self.sysauth = ''
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
_LOGGER.error("An unknown error happened "
|
||||||
|
"while fetching data.")
|
||||||
|
return False
|
||||||
|
except ValueError:
|
||||||
|
_LOGGER.error("Router didn't respond with JSON. "
|
||||||
|
"Check if credentials are correct.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if result:
|
||||||
|
self.last_results = {
|
||||||
|
device['mac'].replace('-', ':'): device['mac']
|
||||||
|
for device in result
|
||||||
|
}
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
173
homeassistant/components/device_tracker/ubus.py
Normal file
173
homeassistant/components/device_tracker/ubus.py
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.device_tracker.ubus
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Device tracker platform that supports scanning a OpenWRT router for device
|
||||||
|
presence.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/device_tracker.ubus.html
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from datetime import timedelta
|
||||||
|
import re
|
||||||
|
import threading
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
|
|
||||||
|
# Return cached results if last scan was less then this time ago
|
||||||
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_scanner(hass, config):
|
||||||
|
""" Validates config and returns a Luci scanner. """
|
||||||
|
if not validate_config(config,
|
||||||
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
|
_LOGGER):
|
||||||
|
return None
|
||||||
|
|
||||||
|
scanner = UbusDeviceScanner(config[DOMAIN])
|
||||||
|
|
||||||
|
return scanner if scanner.success_init else None
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
class UbusDeviceScanner(object):
|
||||||
|
"""
|
||||||
|
This class queries a wireless router running OpenWrt firmware
|
||||||
|
for connected devices. Adapted from Tomato scanner.
|
||||||
|
|
||||||
|
Configure your routers' ubus ACL based on following instructions:
|
||||||
|
|
||||||
|
http://wiki.openwrt.org/doc/techref/ubus
|
||||||
|
|
||||||
|
Read only access will be fine.
|
||||||
|
|
||||||
|
To use this class you have to install rpcd-mod-file package
|
||||||
|
in your OpenWrt router:
|
||||||
|
|
||||||
|
opkg install rpcd-mod-file
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
host = config[CONF_HOST]
|
||||||
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
|
self.parse_api_pattern = re.compile(r"(?P<param>\w*) = (?P<value>.*);")
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.last_results = {}
|
||||||
|
self.url = 'http://{}/ubus'.format(host)
|
||||||
|
|
||||||
|
self.session_id = _get_session_id(self.url, username, password)
|
||||||
|
self.hostapd = []
|
||||||
|
self.leasefile = None
|
||||||
|
self.mac2name = None
|
||||||
|
self.success_init = self.session_id is not None
|
||||||
|
|
||||||
|
def scan_devices(self):
|
||||||
|
"""
|
||||||
|
Scans for new devices and return a list containing found device ids.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._update_info()
|
||||||
|
|
||||||
|
return self.last_results
|
||||||
|
|
||||||
|
def get_device_name(self, device):
|
||||||
|
""" Returns the name of the given device or None if we don't know. """
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
if self.leasefile is None:
|
||||||
|
result = _req_json_rpc(self.url, self.session_id,
|
||||||
|
'call', 'uci', 'get',
|
||||||
|
config="dhcp", type="dnsmasq")
|
||||||
|
if result:
|
||||||
|
values = result["values"].values()
|
||||||
|
self.leasefile = next(iter(values))["leasefile"]
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.mac2name is None:
|
||||||
|
result = _req_json_rpc(self.url, self.session_id,
|
||||||
|
'call', 'file', 'read',
|
||||||
|
path=self.leasefile)
|
||||||
|
if result:
|
||||||
|
self.mac2name = dict()
|
||||||
|
for line in result["data"].splitlines():
|
||||||
|
hosts = line.split(" ")
|
||||||
|
self.mac2name[hosts[1].upper()] = hosts[3]
|
||||||
|
else:
|
||||||
|
# Error, handled in the _req_json_rpc
|
||||||
|
return
|
||||||
|
|
||||||
|
return self.mac2name.get(device.upper(), None)
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
|
def _update_info(self):
|
||||||
|
"""
|
||||||
|
Ensures the information from the Luci router is up to date.
|
||||||
|
Returns boolean if scanning successful.
|
||||||
|
"""
|
||||||
|
if not self.success_init:
|
||||||
|
return False
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
_LOGGER.info("Checking ARP")
|
||||||
|
|
||||||
|
if not self.hostapd:
|
||||||
|
hostapd = _req_json_rpc(self.url, self.session_id,
|
||||||
|
'list', 'hostapd.*', '')
|
||||||
|
self.hostapd.extend(hostapd.keys())
|
||||||
|
|
||||||
|
self.last_results = []
|
||||||
|
results = 0
|
||||||
|
for hostapd in self.hostapd:
|
||||||
|
result = _req_json_rpc(self.url, self.session_id,
|
||||||
|
'call', hostapd, 'get_clients')
|
||||||
|
|
||||||
|
if result:
|
||||||
|
results = results + 1
|
||||||
|
self.last_results.extend(result['clients'].keys())
|
||||||
|
|
||||||
|
return bool(results)
|
||||||
|
|
||||||
|
|
||||||
|
def _req_json_rpc(url, session_id, rpcmethod, subsystem, method, **params):
|
||||||
|
""" Perform one JSON RPC operation. """
|
||||||
|
|
||||||
|
data = json.dumps({"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": rpcmethod,
|
||||||
|
"params": [session_id,
|
||||||
|
subsystem,
|
||||||
|
method,
|
||||||
|
params]})
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = requests.post(url, data=data, timeout=5)
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return
|
||||||
|
|
||||||
|
if res.status_code == 200:
|
||||||
|
response = res.json()
|
||||||
|
|
||||||
|
if rpcmethod == "call":
|
||||||
|
return response["result"][1]
|
||||||
|
else:
|
||||||
|
return response["result"]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_session_id(url, username, password):
|
||||||
|
""" Get authentication token for the given host+username+password. """
|
||||||
|
res = _req_json_rpc(url, "00000000000000000000000000000000", 'call',
|
||||||
|
'session', 'login', username=username,
|
||||||
|
password=password)
|
||||||
|
return res["ubus_rpc_session"]
|
@ -19,7 +19,7 @@ from homeassistant.const import (
|
|||||||
|
|
||||||
DOMAIN = "discovery"
|
DOMAIN = "discovery"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
REQUIREMENTS = ['netdisco==0.4.2']
|
REQUIREMENTS = ['netdisco==0.5.1']
|
||||||
|
|
||||||
SCAN_INTERVAL = 300 # seconds
|
SCAN_INTERVAL = 300 # seconds
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ SERVICE_HUE = 'philips_hue'
|
|||||||
SERVICE_CAST = 'google_cast'
|
SERVICE_CAST = 'google_cast'
|
||||||
SERVICE_NETGEAR = 'netgear_router'
|
SERVICE_NETGEAR = 'netgear_router'
|
||||||
SERVICE_SONOS = 'sonos'
|
SERVICE_SONOS = 'sonos'
|
||||||
|
SERVICE_PLEX = 'plex_mediaserver'
|
||||||
|
|
||||||
SERVICE_HANDLERS = {
|
SERVICE_HANDLERS = {
|
||||||
SERVICE_WEMO: "switch",
|
SERVICE_WEMO: "switch",
|
||||||
@ -35,6 +36,7 @@ SERVICE_HANDLERS = {
|
|||||||
SERVICE_HUE: "light",
|
SERVICE_HUE: "light",
|
||||||
SERVICE_NETGEAR: 'device_tracker',
|
SERVICE_NETGEAR: 'device_tracker',
|
||||||
SERVICE_SONOS: 'media_player',
|
SERVICE_SONOS: 'media_player',
|
||||||
|
SERVICE_PLEX: 'media_player',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -88,6 +90,7 @@ def setup(hass, config):
|
|||||||
ATTR_DISCOVERED: info
|
ATTR_DISCOVERED: info
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
def start_discovery(event):
|
def start_discovery(event):
|
||||||
""" Start discovering. """
|
""" Start discovering. """
|
||||||
netdisco = DiscoveryService(SCAN_INTERVAL)
|
netdisco = DiscoveryService(SCAN_INTERVAL)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.downloader
|
homeassistant.components.downloader
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides functionality to download files.
|
Provides functionality to download files.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/downloader.html
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
@ -42,6 +44,10 @@ def setup(hass, config):
|
|||||||
|
|
||||||
download_path = config[DOMAIN][CONF_DOWNLOAD_DIR]
|
download_path = config[DOMAIN][CONF_DOWNLOAD_DIR]
|
||||||
|
|
||||||
|
# If path is relative, we assume relative to HASS config dir
|
||||||
|
if not os.path.isabs(download_path):
|
||||||
|
download_path = hass.config.path(download_path)
|
||||||
|
|
||||||
if not os.path.isdir(download_path):
|
if not os.path.isdir(download_path):
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||||
VERSION = "c4722afa376379bc4457d54bb9a38cee"
|
VERSION = "beb922c55bb26ea576581b453f6d7c04"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
Subproject commit 6989009b2d59e39fd39b3025ff5899877f618bd3
|
Subproject commit 24623ff26ab8cbf7b39f0a25c26d9d991063b61a
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
File diff suppressed because one or more lines are too long
@ -1,10 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.group
|
homeassistant.components.group
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides functionality to group devices that can be turned on or off.
|
Provides functionality to group devices that can be turned on or off.
|
||||||
"""
|
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/group.html
|
||||||
|
"""
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
from homeassistant.helpers import generate_entity_id
|
from homeassistant.helpers import generate_entity_id
|
||||||
from homeassistant.helpers.event import track_state_change
|
from homeassistant.helpers.event import track_state_change
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.history
|
homeassistant.components.history
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provide pre-made queries on top of the recorder component.
|
Provide pre-made queries on top of the recorder component.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/history.html
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -1,76 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.httpinterface
|
homeassistant.components.http
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This module provides an API and a HTTP interface for debug purposes.
|
This module provides an API and a HTTP interface for debug purposes.
|
||||||
|
|
||||||
By default it will run on port 8123.
|
For more details about the RESTful API, please refer to the documentation at
|
||||||
|
https://home-assistant.io/developers/api.html
|
||||||
All API calls have to be accompanied by an 'api_password' parameter and will
|
|
||||||
return JSON. If successful calls will return status code 200 or 201.
|
|
||||||
|
|
||||||
Other status codes that can occur are:
|
|
||||||
- 400 (Bad Request)
|
|
||||||
- 401 (Unauthorized)
|
|
||||||
- 404 (Not Found)
|
|
||||||
- 405 (Method not allowed)
|
|
||||||
|
|
||||||
The api supports the following actions:
|
|
||||||
|
|
||||||
/api - GET
|
|
||||||
Returns message if API is up and running.
|
|
||||||
Example result:
|
|
||||||
{
|
|
||||||
"message": "API running."
|
|
||||||
}
|
|
||||||
|
|
||||||
/api/states - GET
|
|
||||||
Returns a list of entities for which a state is available
|
|
||||||
Example result:
|
|
||||||
[
|
|
||||||
{ .. state object .. },
|
|
||||||
{ .. state object .. }
|
|
||||||
]
|
|
||||||
|
|
||||||
/api/states/<entity_id> - GET
|
|
||||||
Returns the current state from an entity
|
|
||||||
Example result:
|
|
||||||
{
|
|
||||||
"attributes": {
|
|
||||||
"next_rising": "07:04:15 29-10-2013",
|
|
||||||
"next_setting": "18:00:31 29-10-2013"
|
|
||||||
},
|
|
||||||
"entity_id": "weather.sun",
|
|
||||||
"last_changed": "23:24:33 28-10-2013",
|
|
||||||
"state": "below_horizon"
|
|
||||||
}
|
|
||||||
|
|
||||||
/api/states/<entity_id> - POST
|
|
||||||
Updates the current state of an entity. Returns status code 201 if successful
|
|
||||||
with location header of updated resource and as body the new state.
|
|
||||||
parameter: new_state - string
|
|
||||||
optional parameter: attributes - JSON encoded object
|
|
||||||
Example result:
|
|
||||||
{
|
|
||||||
"attributes": {
|
|
||||||
"next_rising": "07:04:15 29-10-2013",
|
|
||||||
"next_setting": "18:00:31 29-10-2013"
|
|
||||||
},
|
|
||||||
"entity_id": "weather.sun",
|
|
||||||
"last_changed": "23:24:33 28-10-2013",
|
|
||||||
"state": "below_horizon"
|
|
||||||
}
|
|
||||||
|
|
||||||
/api/events/<event_type> - POST
|
|
||||||
Fires an event with event_type
|
|
||||||
optional parameter: event_data - JSON encoded object
|
|
||||||
Example result:
|
|
||||||
{
|
|
||||||
"message": "Event download_file fired."
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
|
@ -1,23 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.ifttt
|
homeassistant.components.ifttt
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
This component enable you to trigger Maker IFTTT recipes.
|
This component enable you to trigger Maker IFTTT recipes.
|
||||||
Check https://ifttt.com/maker for details.
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use Maker IFTTT you will need to add something like the following to your
|
|
||||||
config/configuration.yaml.
|
|
||||||
|
|
||||||
ifttt:
|
|
||||||
key: xxxxx-x-xxxxxxxxxxxxx
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
key
|
|
||||||
*Required
|
|
||||||
Your api key
|
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/ifttt.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.introduction
|
homeassistant.components.introduction
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Component that will help guide the user taking its first steps.
|
Component that will help guide the user taking its first steps.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/introduction.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.keyboard
|
homeassistant.components.keyboard
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides functionality to emulate keyboard presses on host machine.
|
Provides functionality to emulate keyboard presses on host machine.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/keyboard.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
homeassistant.components.light.hue
|
homeassistant.components.light.hue
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for Hue lights.
|
Support for Hue lights.
|
||||||
|
|
||||||
|
https://home-assistant.io/components/light.hue.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
125
homeassistant/components/light/hyperion.py
Normal file
125
homeassistant/components/light/hyperion.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.light.hyperion
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Support for Hyperion remotes.
|
||||||
|
|
||||||
|
https://home-assistant.io/components/light.hyperion.html
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
import json
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_HOST
|
||||||
|
from homeassistant.components.light import (Light, ATTR_RGB_COLOR)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
REQUIREMENTS = []
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
|
""" Sets up a Hyperion server remote """
|
||||||
|
host = config.get(CONF_HOST, None)
|
||||||
|
port = config.get("port", 19444)
|
||||||
|
device = Hyperion(host, port)
|
||||||
|
if device.setup():
|
||||||
|
add_devices_callback([device])
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Hyperion(Light):
|
||||||
|
""" Represents a Hyperion remote """
|
||||||
|
|
||||||
|
def __init__(self, host, port):
|
||||||
|
self._host = host
|
||||||
|
self._port = port
|
||||||
|
self._name = host
|
||||||
|
self._is_available = True
|
||||||
|
self._rgb_color = [255, 255, 255]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Return the hostname of the server. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rgb_color(self):
|
||||||
|
""" Last RGB color value set. """
|
||||||
|
return self._rgb_color
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
""" True if the device is online. """
|
||||||
|
return self._is_available
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
""" Turn the lights on. """
|
||||||
|
if self._is_available:
|
||||||
|
if ATTR_RGB_COLOR in kwargs:
|
||||||
|
self._rgb_color = kwargs[ATTR_RGB_COLOR]
|
||||||
|
|
||||||
|
self.json_request({"command": "color", "priority": 128,
|
||||||
|
"color": self._rgb_color})
|
||||||
|
|
||||||
|
def turn_off(self, **kwargs):
|
||||||
|
""" Disconnect the remote. """
|
||||||
|
self.json_request({"command": "clearall"})
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
""" Ping the remote. """
|
||||||
|
# just see if the remote port is open
|
||||||
|
self._is_available = self.json_request()
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
""" Get the hostname of the remote. """
|
||||||
|
response = self.json_request({"command": "serverinfo"})
|
||||||
|
if response:
|
||||||
|
self._name = response["info"]["hostname"]
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def json_request(self, request=None, wait_for_response=False):
|
||||||
|
""" Communicate with the json server. """
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(5)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sock.connect((self._host, self._port))
|
||||||
|
except OSError:
|
||||||
|
sock.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not request:
|
||||||
|
# no communication needed, simple presence detection returns True
|
||||||
|
sock.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
sock.send(bytearray(json.dumps(request) + "\n", "utf-8"))
|
||||||
|
try:
|
||||||
|
buf = sock.recv(4096)
|
||||||
|
except socket.timeout:
|
||||||
|
# something is wrong, assume it's offline
|
||||||
|
sock.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
# read until a newline or timeout
|
||||||
|
buffering = True
|
||||||
|
while buffering:
|
||||||
|
if "\n" in str(buf, "utf-8"):
|
||||||
|
response = str(buf, "utf-8").split("\n")[0]
|
||||||
|
buffering = False
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
more = sock.recv(4096)
|
||||||
|
except socket.timeout:
|
||||||
|
more = None
|
||||||
|
if not more:
|
||||||
|
buffering = False
|
||||||
|
response = str(buf, "utf-8")
|
||||||
|
else:
|
||||||
|
buf += more
|
||||||
|
|
||||||
|
sock.close()
|
||||||
|
return json.loads(response)
|
@ -12,22 +12,7 @@ Support for LimitlessLED bulbs, also known as...
|
|||||||
- dekolight
|
- dekolight
|
||||||
- iLight
|
- iLight
|
||||||
|
|
||||||
Configuration:
|
https://home-assistant.io/components/light.limitlessled.html
|
||||||
|
|
||||||
To use limitlessled you will need to add the following to your
|
|
||||||
configuration.yaml file.
|
|
||||||
|
|
||||||
light:
|
|
||||||
platform: limitlessled
|
|
||||||
bridges:
|
|
||||||
- host: 192.168.1.10
|
|
||||||
group_1_name: Living Room
|
|
||||||
group_2_name: Bedroom
|
|
||||||
group_3_name: Office
|
|
||||||
group_3_type: white
|
|
||||||
group_4_name: Kitchen
|
|
||||||
- host: 192.168.1.11
|
|
||||||
group_2_name: Basement
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
homeassistant.components.light.tellstick
|
homeassistant.components.light.tellstick
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for Tellstick lights.
|
Support for Tellstick lights.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/light.tellstick.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
# pylint: disable=no-name-in-module, import-error
|
# pylint: disable=no-name-in-module, import-error
|
||||||
|
@ -1,52 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.light.vera
|
homeassistant.components.light.vera
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for Vera lights. This component is useful if you wish for switches
|
Support for Vera lights.
|
||||||
connected to your Vera controller to appear as lights in Home Assistant.
|
|
||||||
All switches will be added as a light unless you exclude them in the config.
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the Vera lights you will need to add something like the following to
|
|
||||||
your configuration.yaml file.
|
|
||||||
|
|
||||||
light:
|
|
||||||
platform: vera
|
|
||||||
vera_controller_url: http://YOUR_VERA_IP:3480/
|
|
||||||
device_data:
|
|
||||||
12:
|
|
||||||
name: My awesome switch
|
|
||||||
exclude: true
|
|
||||||
13:
|
|
||||||
name: Another switch
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
vera_controller_url
|
|
||||||
*Required
|
|
||||||
This is the base URL of your vera controller including the port number if not
|
|
||||||
running on 80. Example: http://192.168.1.21:3480/
|
|
||||||
|
|
||||||
device_data
|
|
||||||
*Optional
|
|
||||||
This contains an array additional device info for your Vera devices. It is not
|
|
||||||
required and if not specified all lights configured in your Vera controller
|
|
||||||
will be added with default values. You should use the id of your vera device
|
|
||||||
as the key for the device within device_data.
|
|
||||||
|
|
||||||
These are the variables for the device_data array:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
This parameter allows you to override the name of your Vera device in the HA
|
|
||||||
interface, if not specified the value configured for the device in your Vera
|
|
||||||
will be used.
|
|
||||||
|
|
||||||
exclude
|
|
||||||
*Optional
|
|
||||||
This parameter allows you to exclude the specified device from Home Assistant,
|
|
||||||
it should be set to "true" if you want this device excluded.
|
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/light.vera.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from requests.exceptions import RequestException
|
from requests.exceptions import RequestException
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
homeassistant.components.light.wink
|
homeassistant.components.light.wink
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for Wink lights.
|
Support for Wink lights.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/light.wink.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.logbook
|
homeassistant.components.logbook
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Parses events and generates a human log.
|
Parses events and generates a human log.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/logbook.html
|
||||||
"""
|
"""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
@ -28,6 +28,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
|||||||
DISCOVERY_PLATFORMS = {
|
DISCOVERY_PLATFORMS = {
|
||||||
discovery.SERVICE_CAST: 'cast',
|
discovery.SERVICE_CAST: 'cast',
|
||||||
discovery.SERVICE_SONOS: 'sonos',
|
discovery.SERVICE_SONOS: 'sonos',
|
||||||
|
discovery.SERVICE_PLEX: 'plex',
|
||||||
}
|
}
|
||||||
|
|
||||||
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
|
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
|
||||||
|
@ -3,22 +3,8 @@ homeassistant.components.media_player.chromecast
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides functionality to interact with Cast devices on the network.
|
Provides functionality to interact with Cast devices on the network.
|
||||||
|
|
||||||
WARNING: This platform is currently not working due to a changed Cast API.
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/media_player.cast.html
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the chromecast integration you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
media_player:
|
|
||||||
platform: chromecast
|
|
||||||
host: 192.168.1.9
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Optional
|
|
||||||
Use only if you don't want to scan for devices.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -2,42 +2,9 @@
|
|||||||
homeassistant.components.media_player.denon
|
homeassistant.components.media_player.denon
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides an interface to Denon Network Receivers.
|
Provides an interface to Denon Network Receivers.
|
||||||
Developed for a Denon DRA-N5, see
|
|
||||||
http://www.denon.co.uk/chg/product/compactsystems/networkmusicsystems/ceolpiccolo
|
|
||||||
|
|
||||||
A few notes:
|
For more details about this platform, please refer to the documentation at
|
||||||
- As long as this module is active and connected, the receiver does
|
https://home-assistant.io/components/media_player.denon.html
|
||||||
not seem to accept additional telnet connections.
|
|
||||||
|
|
||||||
- Be careful with the volume. 50% or even 100% are very loud.
|
|
||||||
|
|
||||||
- To be able to wake up the receiver, activate the "remote" setting
|
|
||||||
in the receiver's settings.
|
|
||||||
|
|
||||||
- Play and pause are supported, toggling is not possible.
|
|
||||||
|
|
||||||
- Seeking cannot be implemented as the UI sends absolute positions.
|
|
||||||
Only seeking via simulated button presses is possible.
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use your Denon you will need to add something like the following to
|
|
||||||
your config/configuration.yaml:
|
|
||||||
|
|
||||||
media_player:
|
|
||||||
platform: denon
|
|
||||||
name: Music station
|
|
||||||
host: 192.168.0.123
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The ip of the player. Example: 192.168.0.123
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
The name of the device.
|
|
||||||
"""
|
"""
|
||||||
import telnetlib
|
import telnetlib
|
||||||
import logging
|
import logging
|
||||||
@ -67,13 +34,15 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
CONF_HOST)
|
CONF_HOST)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
add_devices([
|
denon = DenonDevice(
|
||||||
DenonDevice(
|
config.get("name", "Music station"),
|
||||||
config.get('name', 'Music station'),
|
config.get("host")
|
||||||
config.get('host'))
|
)
|
||||||
])
|
if denon.update():
|
||||||
|
add_devices([denon])
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class DenonDevice(MediaPlayerDevice):
|
class DenonDevice(MediaPlayerDevice):
|
||||||
@ -84,28 +53,41 @@ class DenonDevice(MediaPlayerDevice):
|
|||||||
def __init__(self, name, host):
|
def __init__(self, name, host):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._host = host
|
self._host = host
|
||||||
self._telnet = telnetlib.Telnet(self._host)
|
self._pwstate = "PWSTANDBY"
|
||||||
|
self._volume = 0
|
||||||
|
self._muted = False
|
||||||
|
self._mediasource = ""
|
||||||
|
|
||||||
def query(self, message):
|
@classmethod
|
||||||
""" Send request and await response from server """
|
def telnet_request(cls, telnet, command):
|
||||||
|
""" Executes `command` and returns the response. """
|
||||||
|
telnet.write(command.encode("ASCII") + b"\r")
|
||||||
|
return telnet.read_until(b"\r", timeout=0.2).decode("ASCII").strip()
|
||||||
|
|
||||||
|
def telnet_command(self, command):
|
||||||
|
""" Establishes a telnet connection and sends `command`. """
|
||||||
|
telnet = telnetlib.Telnet(self._host)
|
||||||
|
telnet.write(command.encode("ASCII") + b"\r")
|
||||||
|
telnet.read_very_eager() # skip response
|
||||||
|
telnet.close()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
try:
|
try:
|
||||||
# unspecified command, should be ignored
|
telnet = telnetlib.Telnet(self._host)
|
||||||
self._telnet.write("?".encode('UTF-8') + b'\r')
|
except ConnectionRefusedError:
|
||||||
except (EOFError, BrokenPipeError, ConnectionResetError):
|
return False
|
||||||
self._telnet.open(self._host)
|
|
||||||
|
|
||||||
self._telnet.read_very_eager() # skip what is not requested
|
self._pwstate = self.telnet_request(telnet, "PW?")
|
||||||
|
# PW? sends also SISTATUS, which is not interesting
|
||||||
|
telnet.read_until(b"\r", timeout=0.2)
|
||||||
|
|
||||||
self._telnet.write(message.encode('ASCII') + b'\r')
|
volume_str = self.telnet_request(telnet, "MV?")[len("MV"):]
|
||||||
# timeout 200ms, defined by protocol
|
self._volume = int(volume_str) / 60
|
||||||
resp = self._telnet.read_until(b'\r', timeout=0.2)\
|
self._muted = (self.telnet_request(telnet, "MU?") == "MUON")
|
||||||
.decode('UTF-8').strip()
|
self._mediasource = self.telnet_request(telnet, "SI?")[len("SI"):]
|
||||||
|
|
||||||
if message == "PW?":
|
telnet.close()
|
||||||
# workaround; PW? sends also SISTATUS
|
return True
|
||||||
self._telnet.read_until(b'\r', timeout=0.2)
|
|
||||||
|
|
||||||
return resp
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -115,10 +97,9 @@ class DenonDevice(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
""" Returns the state of the device. """
|
""" Returns the state of the device. """
|
||||||
pwstate = self.query('PW?')
|
if self._pwstate == "PWSTANDBY":
|
||||||
if pwstate == "PWSTANDBY":
|
|
||||||
return STATE_OFF
|
return STATE_OFF
|
||||||
if pwstate == "PWON":
|
if self._pwstate == "PWON":
|
||||||
return STATE_ON
|
return STATE_ON
|
||||||
|
|
||||||
return STATE_UNKNOWN
|
return STATE_UNKNOWN
|
||||||
@ -126,17 +107,17 @@ class DenonDevice(MediaPlayerDevice):
|
|||||||
@property
|
@property
|
||||||
def volume_level(self):
|
def volume_level(self):
|
||||||
""" Volume level of the media player (0..1). """
|
""" Volume level of the media player (0..1). """
|
||||||
return int(self.query('MV?')[len('MV'):]) / 60
|
return self._volume
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_volume_muted(self):
|
def is_volume_muted(self):
|
||||||
""" Boolean if volume is currently muted. """
|
""" Boolean if volume is currently muted. """
|
||||||
return self.query('MU?') == "MUON"
|
return self._muted
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_title(self):
|
def media_title(self):
|
||||||
""" Current media source. """
|
""" Current media source. """
|
||||||
return self.query('SI?')[len('SI'):]
|
return self._mediasource
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_media_commands(self):
|
def supported_media_commands(self):
|
||||||
@ -145,24 +126,24 @@ class DenonDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
def turn_off(self):
|
def turn_off(self):
|
||||||
""" turn_off media player. """
|
""" turn_off media player. """
|
||||||
self.query('PWSTANDBY')
|
self.telnet_command("PWSTANDBY")
|
||||||
|
|
||||||
def volume_up(self):
|
def volume_up(self):
|
||||||
""" volume_up media player. """
|
""" volume_up media player. """
|
||||||
self.query('MVUP')
|
self.telnet_command("MVUP")
|
||||||
|
|
||||||
def volume_down(self):
|
def volume_down(self):
|
||||||
""" volume_down media player. """
|
""" volume_down media player. """
|
||||||
self.query('MVDOWN')
|
self.telnet_command("MVDOWN")
|
||||||
|
|
||||||
def set_volume_level(self, volume):
|
def set_volume_level(self, volume):
|
||||||
""" set volume level, range 0..1. """
|
""" set volume level, range 0..1. """
|
||||||
# 60dB max
|
# 60dB max
|
||||||
self.query('MV' + str(round(volume * 60)).zfill(2))
|
self.telnet_command("MV" + str(round(volume * 60)).zfill(2))
|
||||||
|
|
||||||
def mute_volume(self, mute):
|
def mute_volume(self, mute):
|
||||||
""" mute (true) or unmute (false) media player. """
|
""" mute (true) or unmute (false) media player. """
|
||||||
self.query('MU' + ('ON' if mute else 'OFF'))
|
self.telnet_command("MU" + ("ON" if mute else "OFF"))
|
||||||
|
|
||||||
def media_play_pause(self):
|
def media_play_pause(self):
|
||||||
""" media_play_pause media player. """
|
""" media_play_pause media player. """
|
||||||
@ -170,22 +151,22 @@ class DenonDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
def media_play(self):
|
def media_play(self):
|
||||||
""" media_play media player. """
|
""" media_play media player. """
|
||||||
self.query('NS9A')
|
self.telnet_command("NS9A")
|
||||||
|
|
||||||
def media_pause(self):
|
def media_pause(self):
|
||||||
""" media_pause media player. """
|
""" media_pause media player. """
|
||||||
self.query('NS9B')
|
self.telnet_command("NS9B")
|
||||||
|
|
||||||
def media_next_track(self):
|
def media_next_track(self):
|
||||||
""" Send next track command. """
|
""" Send next track command. """
|
||||||
self.query('NS9D')
|
self.telnet_command("NS9D")
|
||||||
|
|
||||||
def media_previous_track(self):
|
def media_previous_track(self):
|
||||||
self.query('NS9E')
|
self.telnet_command("NS9E")
|
||||||
|
|
||||||
def media_seek(self, position):
|
def media_seek(self, position):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def turn_on(self):
|
def turn_on(self):
|
||||||
""" turn the media player on. """
|
""" turn the media player on. """
|
||||||
self.query('PWON')
|
self.telnet_command("PWON")
|
||||||
|
190
homeassistant/components/media_player/firetv.py
Normal file
190
homeassistant/components/media_player/firetv.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.media_player.firetv
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Provides functionality to interact with FireTV devices.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/media_player.firetv.html
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
STATE_PLAYING, STATE_PAUSED, STATE_IDLE, STATE_OFF,
|
||||||
|
STATE_UNKNOWN, STATE_STANDBY)
|
||||||
|
|
||||||
|
from homeassistant.components.media_player import (
|
||||||
|
MediaPlayerDevice,
|
||||||
|
SUPPORT_PAUSE, SUPPORT_VOLUME_SET,
|
||||||
|
SUPPORT_TURN_ON, SUPPORT_TURN_OFF,
|
||||||
|
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK)
|
||||||
|
|
||||||
|
SUPPORT_FIRETV = SUPPORT_PAUSE | \
|
||||||
|
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PREVIOUS_TRACK | \
|
||||||
|
SUPPORT_NEXT_TRACK | SUPPORT_VOLUME_SET
|
||||||
|
|
||||||
|
DOMAIN = 'firetv'
|
||||||
|
DEVICE_LIST_URL = 'http://{0}/devices/list'
|
||||||
|
DEVICE_STATE_URL = 'http://{0}/devices/state/{1}'
|
||||||
|
DEVICE_ACTION_URL = 'http://{0}/devices/action/{1}/{2}'
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up the FireTV platform. """
|
||||||
|
host = config.get('host', 'localhost:5556')
|
||||||
|
device_id = config.get('device', 'default')
|
||||||
|
try:
|
||||||
|
response = requests.get(DEVICE_LIST_URL.format(host)).json()
|
||||||
|
if device_id in response['devices'].keys():
|
||||||
|
add_devices([
|
||||||
|
FireTVDevice(
|
||||||
|
host,
|
||||||
|
device_id,
|
||||||
|
config.get('name', 'Amazon Fire TV')
|
||||||
|
)
|
||||||
|
])
|
||||||
|
_LOGGER.info(
|
||||||
|
'Device %s accessible and ready for control', device_id)
|
||||||
|
else:
|
||||||
|
_LOGGER.warn(
|
||||||
|
'Device %s is not registered with firetv-server', device_id)
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
_LOGGER.error('Could not connect to firetv-server at %s', host)
|
||||||
|
|
||||||
|
|
||||||
|
class FireTV(object):
|
||||||
|
""" firetv-server client.
|
||||||
|
|
||||||
|
Should a native Python 3 ADB module become available, python-firetv can
|
||||||
|
support Python 3, it can be added as a dependency, and this class can be
|
||||||
|
dispensed of.
|
||||||
|
|
||||||
|
For now, it acts as a client to the firetv-server HTTP server (which must
|
||||||
|
be running via Python 2).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, host, device_id):
|
||||||
|
self.host = host
|
||||||
|
self.device_id = device_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Get the device state. An exception means UNKNOWN state. """
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
DEVICE_STATE_URL.format(
|
||||||
|
self.host,
|
||||||
|
self.device_id
|
||||||
|
)
|
||||||
|
).json()
|
||||||
|
return response.get('state', STATE_UNKNOWN)
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
_LOGGER.error(
|
||||||
|
'Could not retrieve device state for %s', self.device_id)
|
||||||
|
return STATE_UNKNOWN
|
||||||
|
|
||||||
|
def action(self, action_id):
|
||||||
|
""" Perform an action on the device. """
|
||||||
|
try:
|
||||||
|
requests.get(
|
||||||
|
DEVICE_ACTION_URL.format(
|
||||||
|
self.host,
|
||||||
|
self.device_id,
|
||||||
|
action_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
_LOGGER.error(
|
||||||
|
'Action request for %s was not accepted for device %s',
|
||||||
|
action_id, self.device_id)
|
||||||
|
|
||||||
|
|
||||||
|
class FireTVDevice(MediaPlayerDevice):
|
||||||
|
""" Represents an Amazon Fire TV device on the network. """
|
||||||
|
|
||||||
|
def __init__(self, host, device, name):
|
||||||
|
self._firetv = FireTV(host, device)
|
||||||
|
self._name = name
|
||||||
|
self._state = STATE_UNKNOWN
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Get the device name. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
""" Device should be polled. """
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_media_commands(self):
|
||||||
|
""" Flags of media commands that are supported. """
|
||||||
|
return SUPPORT_FIRETV
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" State of the player. """
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
""" Update device state. """
|
||||||
|
self._state = {
|
||||||
|
'idle': STATE_IDLE,
|
||||||
|
'off': STATE_OFF,
|
||||||
|
'play': STATE_PLAYING,
|
||||||
|
'pause': STATE_PAUSED,
|
||||||
|
'standby': STATE_STANDBY,
|
||||||
|
'disconnected': STATE_UNKNOWN,
|
||||||
|
}.get(self._firetv.state, STATE_UNKNOWN)
|
||||||
|
|
||||||
|
def turn_on(self):
|
||||||
|
""" Turns on the device. """
|
||||||
|
self._firetv.action('turn_on')
|
||||||
|
|
||||||
|
def turn_off(self):
|
||||||
|
""" Turns off the device. """
|
||||||
|
self._firetv.action('turn_off')
|
||||||
|
|
||||||
|
def media_play(self):
|
||||||
|
""" Send play command. """
|
||||||
|
self._firetv.action('media_play')
|
||||||
|
|
||||||
|
def media_pause(self):
|
||||||
|
""" Send pause command. """
|
||||||
|
self._firetv.action('media_pause')
|
||||||
|
|
||||||
|
def media_play_pause(self):
|
||||||
|
""" Send play/pause command. """
|
||||||
|
self._firetv.action('media_play_pause')
|
||||||
|
|
||||||
|
def volume_up(self):
|
||||||
|
""" Send volume up command. """
|
||||||
|
self._firetv.action('volume_up')
|
||||||
|
|
||||||
|
def volume_down(self):
|
||||||
|
""" Send volume down command. """
|
||||||
|
self._firetv.action('volume_down')
|
||||||
|
|
||||||
|
def media_previous_track(self):
|
||||||
|
""" Send previous track command (results in rewind). """
|
||||||
|
self._firetv.action('media_previous')
|
||||||
|
|
||||||
|
def media_next_track(self):
|
||||||
|
""" Send next track command (results in fast-forward). """
|
||||||
|
self._firetv.action('media_next')
|
||||||
|
|
||||||
|
def media_seek(self, position):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def mute_volume(self, mute):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def play_youtube(self, media_id):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def set_volume_level(self, volume):
|
||||||
|
raise NotImplementedError()
|
@ -1,36 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.media_player.itunes
|
homeassistant.components.media_player.itunes
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides an interface to iTunes-API (https://github.com/maddox/itunes-api)
|
Provides an interface to iTunes API.
|
||||||
|
|
||||||
The iTunes media player will allow you to control your iTunes instance. You
|
|
||||||
can play/pause/next/previous/mute, adjust volume, etc.
|
|
||||||
|
|
||||||
In addition to controlling iTunes, your available AirPlay endpoints will be
|
|
||||||
added as media players as well. You can then individually address them append
|
|
||||||
turn them on, turn them off, or adjust their volume.
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use iTunes you will need to add something like the following to
|
|
||||||
your configuration.yaml file.
|
|
||||||
|
|
||||||
media_player:
|
|
||||||
platform: itunes
|
|
||||||
name: iTunes
|
|
||||||
host: http://192.168.1.16
|
|
||||||
port: 8181
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
The name of the device.
|
|
||||||
|
|
||||||
url
|
|
||||||
*Required
|
|
||||||
URL of your running version of iTunes-API. Example: http://192.168.1.50:8181
|
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/media_player.itunes.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -157,11 +131,9 @@ class Itunes(object):
|
|||||||
path = '/airplay_devices/' + device_id + '/volume'
|
path = '/airplay_devices/' + device_id + '/volume'
|
||||||
return self._request('PUT', path, {'level': level})
|
return self._request('PUT', path, {'level': level})
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
# pylint: disable=abstract-method
|
# pylint: disable=unused-argument, abstract-method
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the itunes platform. """
|
""" Sets up the itunes platform. """
|
||||||
|
|
||||||
@ -179,7 +151,6 @@ class ItunesDevice(MediaPlayerDevice):
|
|||||||
""" Represents a iTunes-API instance. """
|
""" Represents a iTunes-API instance. """
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
|
|
||||||
def __init__(self, name, host, port, add_devices):
|
def __init__(self, name, host, port, add_devices):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._host = host
|
self._host = host
|
||||||
|
@ -3,35 +3,8 @@ homeassistant.components.media_player.kodi
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides an interface to the XBMC/Kodi JSON-RPC API
|
Provides an interface to the XBMC/Kodi JSON-RPC API
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/media_player.kodi.html
|
||||||
To use the Kodi you will need to add something like the following to
|
|
||||||
your configuration.yaml file.
|
|
||||||
|
|
||||||
media_player:
|
|
||||||
platform: kodi
|
|
||||||
name: Kodi
|
|
||||||
url: http://192.168.0.123/jsonrpc
|
|
||||||
user: kodi
|
|
||||||
password: my_secure_password
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
The name of the device.
|
|
||||||
|
|
||||||
url
|
|
||||||
*Required
|
|
||||||
The URL of the XBMC/Kodi JSON-RPC API. Example: http://192.168.0.123/jsonrpc
|
|
||||||
|
|
||||||
user
|
|
||||||
*Optional
|
|
||||||
The XBMC/Kodi HTTP username.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Optional
|
|
||||||
The XBMC/Kodi HTTP password.
|
|
||||||
"""
|
"""
|
||||||
import urllib
|
import urllib
|
||||||
import logging
|
import logging
|
||||||
|
@ -3,35 +3,8 @@ homeassistant.components.media_player.mpd
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides functionality to interact with a Music Player Daemon.
|
Provides functionality to interact with a Music Player Daemon.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/media_player.mpd.html
|
||||||
To use MPD you will need to add something like the following to your
|
|
||||||
configuration.yaml file.
|
|
||||||
|
|
||||||
media_player:
|
|
||||||
platform: mpd
|
|
||||||
server: 127.0.0.1
|
|
||||||
port: 6600
|
|
||||||
location: bedroom
|
|
||||||
password: superSecretPassword123
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
server
|
|
||||||
*Required
|
|
||||||
IP address of the Music Player Daemon. Example: 192.168.1.32
|
|
||||||
|
|
||||||
port
|
|
||||||
*Optional
|
|
||||||
Port of the Music Player Daemon, defaults to 6600. Example: 6600
|
|
||||||
|
|
||||||
location
|
|
||||||
*Optional
|
|
||||||
Location of your Music Player Daemon.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Optional
|
|
||||||
Password for your Music Player Daemon.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
@ -6,38 +6,114 @@ Provides an interface to the Plex API.
|
|||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/media_player.plex.html
|
https://home-assistant.io/components/media_player.plex.html
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
import homeassistant.util as util
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK,
|
MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK,
|
||||||
SUPPORT_NEXT_TRACK, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
|
SUPPORT_NEXT_TRACK, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_UNKNOWN)
|
DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_PLAYING,
|
||||||
import homeassistant.util as util
|
STATE_PAUSED, STATE_OFF, STATE_UNKNOWN)
|
||||||
|
|
||||||
REQUIREMENTS = ['https://github.com/adrienbrault/python-plexapi/archive/'
|
REQUIREMENTS = ['plexapi==1.1.0']
|
||||||
'df2d0847e801d6d5cda920326d693cf75f304f1a.zip'
|
|
||||||
'#python-plexapi==1.0.2']
|
|
||||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
||||||
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
|
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
|
||||||
|
|
||||||
|
PLEX_CONFIG_FILE = 'plex.conf'
|
||||||
|
|
||||||
|
# Map ip to request id for configuring
|
||||||
|
_CONFIGURING = {}
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
|
SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=abstract-method, unused-argument
|
def config_from_file(filename, config=None):
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
''' Small configuration file management function'''
|
||||||
""" Sets up the plex platform. """
|
if config:
|
||||||
from plexapi.myplex import MyPlexUser
|
# We're writing configuration
|
||||||
from plexapi.exceptions import BadRequest
|
try:
|
||||||
|
with open(filename, 'w') as fdesc:
|
||||||
|
fdesc.write(json.dumps(config))
|
||||||
|
except IOError as error:
|
||||||
|
_LOGGER.error('Saving config file failed: %s', error)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# We're reading config
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
try:
|
||||||
|
with open(filename, 'r') as fdesc:
|
||||||
|
return json.loads(fdesc.read())
|
||||||
|
except IOError as error:
|
||||||
|
_LOGGER.error('Reading config file failed: %s', error)
|
||||||
|
# This won't work yet
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=abstract-method, unused-argument
|
||||||
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
|
""" Sets up the plex platform. """
|
||||||
|
|
||||||
|
config = config_from_file(hass.config.path(PLEX_CONFIG_FILE))
|
||||||
|
if len(config):
|
||||||
|
# Setup a configured PlexServer
|
||||||
|
host, token = config.popitem()
|
||||||
|
token = token['token']
|
||||||
|
# Via discovery
|
||||||
|
elif discovery_info is not None:
|
||||||
|
# Parse discovery data
|
||||||
|
host = urlparse(discovery_info[1]).netloc
|
||||||
|
_LOGGER.info('Discovered PLEX server: %s', host)
|
||||||
|
|
||||||
|
if host in _CONFIGURING:
|
||||||
|
return
|
||||||
|
token = None
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
setup_plexserver(host, token, hass, add_devices_callback)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-branches
|
||||||
|
def setup_plexserver(host, token, hass, add_devices_callback):
|
||||||
|
''' Setup a plexserver based on host parameter'''
|
||||||
|
import plexapi.server
|
||||||
|
import plexapi.exceptions
|
||||||
|
|
||||||
|
try:
|
||||||
|
plexserver = plexapi.server.PlexServer('http://%s' % host, token)
|
||||||
|
except (plexapi.exceptions.BadRequest,
|
||||||
|
plexapi.exceptions.Unauthorized,
|
||||||
|
plexapi.exceptions.NotFound) as error:
|
||||||
|
_LOGGER.info(error)
|
||||||
|
# No token or wrong token
|
||||||
|
request_configuration(host, hass, add_devices_callback)
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we came here and configuring this host, mark as done
|
||||||
|
if host in _CONFIGURING:
|
||||||
|
request_id = _CONFIGURING.pop(host)
|
||||||
|
configurator = get_component('configurator')
|
||||||
|
configurator.request_done(request_id)
|
||||||
|
_LOGGER.info('Discovery configuration done!')
|
||||||
|
|
||||||
|
# Save config
|
||||||
|
if not config_from_file(
|
||||||
|
hass.config.path(PLEX_CONFIG_FILE),
|
||||||
|
{host: {'token': token}}):
|
||||||
|
_LOGGER.error('failed to save config file')
|
||||||
|
|
||||||
|
_LOGGER.info('Connected to: htts://%s', host)
|
||||||
|
|
||||||
name = config.get('name', '')
|
|
||||||
user = config.get('user', '')
|
|
||||||
password = config.get('password', '')
|
|
||||||
plexuser = MyPlexUser.signin(user, password)
|
|
||||||
plexserver = plexuser.getResource(name).connect()
|
|
||||||
plex_clients = {}
|
plex_clients = {}
|
||||||
plex_sessions = {}
|
plex_sessions = {}
|
||||||
|
|
||||||
@ -45,34 +121,34 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
def update_devices():
|
def update_devices():
|
||||||
""" Updates the devices objects. """
|
""" Updates the devices objects. """
|
||||||
try:
|
try:
|
||||||
devices = plexuser.devices()
|
devices = plexserver.clients()
|
||||||
except BadRequest:
|
except plexapi.exceptions.BadRequest:
|
||||||
_LOGGER.exception("Error listing plex devices")
|
_LOGGER.exception("Error listing plex devices")
|
||||||
return
|
return
|
||||||
|
|
||||||
new_plex_clients = []
|
new_plex_clients = []
|
||||||
for device in devices:
|
for device in devices:
|
||||||
if (all(x not in ['client', 'player'] for x in device.provides)
|
# For now, let's allow all deviceClass types
|
||||||
or 'PlexAPI' == device.product):
|
if device.deviceClass in ['badClient']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if device.clientIdentifier not in plex_clients:
|
if device.machineIdentifier not in plex_clients:
|
||||||
new_client = PlexClient(device, plex_sessions, update_devices,
|
new_client = PlexClient(device, plex_sessions, update_devices,
|
||||||
update_sessions)
|
update_sessions)
|
||||||
plex_clients[device.clientIdentifier] = new_client
|
plex_clients[device.machineIdentifier] = new_client
|
||||||
new_plex_clients.append(new_client)
|
new_plex_clients.append(new_client)
|
||||||
else:
|
else:
|
||||||
plex_clients[device.clientIdentifier].set_device(device)
|
plex_clients[device.machineIdentifier].set_device(device)
|
||||||
|
|
||||||
if new_plex_clients:
|
if new_plex_clients:
|
||||||
add_devices(new_plex_clients)
|
add_devices_callback(new_plex_clients)
|
||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
|
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
|
||||||
def update_sessions():
|
def update_sessions():
|
||||||
""" Updates the sessions objects. """
|
""" Updates the sessions objects. """
|
||||||
try:
|
try:
|
||||||
sessions = plexserver.sessions()
|
sessions = plexserver.sessions()
|
||||||
except BadRequest:
|
except plexapi.exceptions.BadRequest:
|
||||||
_LOGGER.exception("Error listing plex sessions")
|
_LOGGER.exception("Error listing plex sessions")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -84,10 +160,34 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
update_sessions()
|
update_sessions()
|
||||||
|
|
||||||
|
|
||||||
|
def request_configuration(host, hass, add_devices_callback):
|
||||||
|
""" Request configuration steps from the user. """
|
||||||
|
configurator = get_component('configurator')
|
||||||
|
|
||||||
|
# We got an error if this method is called while we are configuring
|
||||||
|
if host in _CONFIGURING:
|
||||||
|
configurator.notify_errors(
|
||||||
|
_CONFIGURING[host], "Failed to register, please try again.")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def plex_configuration_callback(data):
|
||||||
|
""" Actions to do when our configuration callback is called. """
|
||||||
|
setup_plexserver(host, data.get('token'), hass, add_devices_callback)
|
||||||
|
|
||||||
|
_CONFIGURING[host] = configurator.request_config(
|
||||||
|
hass, "Plex Media Server", plex_configuration_callback,
|
||||||
|
description=('Enter the X-Plex-Token'),
|
||||||
|
description_image="/static/images/config_plex_mediaserver.png",
|
||||||
|
submit_caption="Confirm",
|
||||||
|
fields=[{'id': 'token', 'name': 'X-Plex-Token', 'type': ''}]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlexClient(MediaPlayerDevice):
|
class PlexClient(MediaPlayerDevice):
|
||||||
""" Represents a Plex device. """
|
""" Represents a Plex device. """
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods, attribute-defined-outside-init
|
||||||
def __init__(self, device, plex_sessions, update_devices, update_sessions):
|
def __init__(self, device, plex_sessions, update_devices, update_sessions):
|
||||||
self.plex_sessions = plex_sessions
|
self.plex_sessions = plex_sessions
|
||||||
self.update_devices = update_devices
|
self.update_devices = update_devices
|
||||||
@ -99,17 +199,23 @@ class PlexClient(MediaPlayerDevice):
|
|||||||
self.device = device
|
self.device = device
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def session(self):
|
def unique_id(self):
|
||||||
""" Returns the session, if any. """
|
""" Returns the id of this plex client """
|
||||||
if self.device.clientIdentifier not in self.plex_sessions:
|
return "{}.{}".format(
|
||||||
return None
|
self.__class__, self.device.machineIdentifier or self.device.name)
|
||||||
|
|
||||||
return self.plex_sessions[self.device.clientIdentifier]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" Returns the name of the device. """
|
""" Returns the name of the device. """
|
||||||
return self.device.name or self.device.product or self.device.device
|
return self.device.name or DEVICE_DEFAULT_NAME
|
||||||
|
|
||||||
|
@property
|
||||||
|
def session(self):
|
||||||
|
""" Returns the session, if any. """
|
||||||
|
if self.device.machineIdentifier not in self.plex_sessions:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.plex_sessions[self.device.machineIdentifier]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
@ -120,7 +226,8 @@ class PlexClient(MediaPlayerDevice):
|
|||||||
return STATE_PLAYING
|
return STATE_PLAYING
|
||||||
elif state == 'paused':
|
elif state == 'paused':
|
||||||
return STATE_PAUSED
|
return STATE_PAUSED
|
||||||
elif self.device.isReachable:
|
# This is nasty. Need to find a way to determine alive
|
||||||
|
elif self.device:
|
||||||
return STATE_IDLE
|
return STATE_IDLE
|
||||||
else:
|
else:
|
||||||
return STATE_OFF
|
return STATE_OFF
|
||||||
@ -196,16 +303,16 @@ class PlexClient(MediaPlayerDevice):
|
|||||||
|
|
||||||
def media_play(self):
|
def media_play(self):
|
||||||
""" media_play media player. """
|
""" media_play media player. """
|
||||||
self.device.play({'type': 'video'})
|
self.device.play()
|
||||||
|
|
||||||
def media_pause(self):
|
def media_pause(self):
|
||||||
""" media_pause media player. """
|
""" media_pause media player. """
|
||||||
self.device.pause({'type': 'video'})
|
self.device.pause()
|
||||||
|
|
||||||
def media_next_track(self):
|
def media_next_track(self):
|
||||||
""" Send next track command. """
|
""" Send next track command. """
|
||||||
self.device.skipNext({'type': 'video'})
|
self.device.skipNext()
|
||||||
|
|
||||||
def media_previous_track(self):
|
def media_previous_track(self):
|
||||||
""" Send previous track command. """
|
""" Send previous track command. """
|
||||||
self.device.skipPrevious({'type': 'video'})
|
self.device.skipPrevious()
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.media_player.sonos
|
homeassistant.components.media_player.sonos
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides an interface to Sonos players (via SoCo)
|
Provides an interface to Sonos players (via SoCo)
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/media_player.sonos.html
|
||||||
To use SoCo, add something like this to your configuration:
|
|
||||||
|
|
||||||
media_player:
|
|
||||||
platform: sonos
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@ -56,8 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes, too-many-public-methods
|
||||||
# pylint: disable=too-many-public-methods
|
|
||||||
# pylint: disable=abstract-method
|
# pylint: disable=abstract-method
|
||||||
class SonosDevice(MediaPlayerDevice):
|
class SonosDevice(MediaPlayerDevice):
|
||||||
""" Represents a Sonos device. """
|
""" Represents a Sonos device. """
|
||||||
@ -74,7 +67,7 @@ class SonosDevice(MediaPlayerDevice):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def update_sonos(self, now):
|
def update_sonos(self, now):
|
||||||
""" Updates state, called by track_utc_time_change """
|
""" Updates state, called by track_utc_time_change. """
|
||||||
self.update_ha_state(True)
|
self.update_ha_state(True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -162,31 +155,31 @@ class SonosDevice(MediaPlayerDevice):
|
|||||||
return SUPPORT_SONOS
|
return SUPPORT_SONOS
|
||||||
|
|
||||||
def turn_off(self):
|
def turn_off(self):
|
||||||
""" turn_off media player. """
|
""" Turn off media player. """
|
||||||
self._player.pause()
|
self._player.pause()
|
||||||
|
|
||||||
def volume_up(self):
|
def volume_up(self):
|
||||||
""" volume_up media player. """
|
""" Volume up media player. """
|
||||||
self._player.volume += 1
|
self._player.volume += 1
|
||||||
|
|
||||||
def volume_down(self):
|
def volume_down(self):
|
||||||
""" volume_down media player. """
|
""" Volume down media player. """
|
||||||
self._player.volume -= 1
|
self._player.volume -= 1
|
||||||
|
|
||||||
def set_volume_level(self, volume):
|
def set_volume_level(self, volume):
|
||||||
""" set volume level, range 0..1. """
|
""" Set volume level, range 0..1. """
|
||||||
self._player.volume = str(int(volume * 100))
|
self._player.volume = str(int(volume * 100))
|
||||||
|
|
||||||
def mute_volume(self, mute):
|
def mute_volume(self, mute):
|
||||||
""" mute (true) or unmute (false) media player. """
|
""" Mute (true) or unmute (false) media player. """
|
||||||
self._player.mute = mute
|
self._player.mute = mute
|
||||||
|
|
||||||
def media_play(self):
|
def media_play(self):
|
||||||
""" media_play media player. """
|
""" Send paly command. """
|
||||||
self._player.play()
|
self._player.play()
|
||||||
|
|
||||||
def media_pause(self):
|
def media_pause(self):
|
||||||
""" media_pause media player. """
|
""" Send pause command. """
|
||||||
self._player.pause()
|
self._player.pause()
|
||||||
|
|
||||||
def media_next_track(self):
|
def media_next_track(self):
|
||||||
@ -202,5 +195,5 @@ class SonosDevice(MediaPlayerDevice):
|
|||||||
self._player.seek(str(datetime.timedelta(seconds=int(position))))
|
self._player.seek(str(datetime.timedelta(seconds=int(position))))
|
||||||
|
|
||||||
def turn_on(self):
|
def turn_on(self):
|
||||||
""" turn the media player on. """
|
""" Turn the media player on. """
|
||||||
self._player.play()
|
self._player.play()
|
||||||
|
@ -1,39 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.media_player.squeezebox
|
homeassistant.components.media_player.squeezebox
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides an interface to the Logitech SqueezeBox API
|
Provides an interface to the Logitech SqueezeBox API
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/media_player.squeezebox.html
|
||||||
To use SqueezeBox add something something like the following to your
|
|
||||||
configuration.yaml file.
|
|
||||||
|
|
||||||
media_player:
|
|
||||||
platform: squeezebox
|
|
||||||
host: 192.168.1.21
|
|
||||||
port: 9090
|
|
||||||
username: user
|
|
||||||
password: password
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The host name or address of the Logitech Media Server.
|
|
||||||
|
|
||||||
port
|
|
||||||
*Optional
|
|
||||||
Telnet port to Logitech Media Server, default 9090.
|
|
||||||
|
|
||||||
usermame
|
|
||||||
*Optional
|
|
||||||
Username, if password protection is enabled.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Optional
|
|
||||||
Password, if password protection is enabled.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import telnetlib
|
import telnetlib
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
@ -291,7 +263,7 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||||||
|
|
||||||
def media_pause(self):
|
def media_pause(self):
|
||||||
""" media_pause media player. """
|
""" media_pause media player. """
|
||||||
self._lms.query(self._id, 'pause', '0')
|
self._lms.query(self._id, 'pause', '1')
|
||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
|
|
||||||
def media_next_track(self):
|
def media_next_track(self):
|
||||||
|
@ -3,27 +3,8 @@ homeassistant.components.modbus
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Modbus component, using pymodbus (python3 branch).
|
Modbus component, using pymodbus (python3 branch).
|
||||||
|
|
||||||
Configuration:
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/modbus.html
|
||||||
To use the Modbus component you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
#Modbus TCP
|
|
||||||
modbus:
|
|
||||||
type: tcp
|
|
||||||
host: 127.0.0.1
|
|
||||||
port: 2020
|
|
||||||
|
|
||||||
#Modbus RTU
|
|
||||||
modbus:
|
|
||||||
type: serial
|
|
||||||
method: rtu
|
|
||||||
port: /dev/ttyUSB0
|
|
||||||
baudrate: 9600
|
|
||||||
stopbits: 1
|
|
||||||
bytesize: 8
|
|
||||||
parity: N
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,52 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.mqtt
|
homeassistant.components.mqtt
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
MQTT component, using paho-mqtt. This component needs a MQTT broker like
|
MQTT component, using paho-mqtt.
|
||||||
Mosquitto or Mosca. The Eclipse Foundation is running a public MQTT server
|
|
||||||
at iot.eclipse.org. If you prefer to use that one, keep in mind to adjust
|
|
||||||
the topic/client ID and that your messages are public.
|
|
||||||
|
|
||||||
Configuration:
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/mqtt.html
|
||||||
To use MQTT you will need to add something like the following to your
|
|
||||||
config/configuration.yaml.
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
broker: 127.0.0.1
|
|
||||||
|
|
||||||
Or, if you want more options:
|
|
||||||
|
|
||||||
mqtt:
|
|
||||||
broker: 127.0.0.1
|
|
||||||
port: 1883
|
|
||||||
client_id: home-assistant-1
|
|
||||||
keepalive: 60
|
|
||||||
username: your_username
|
|
||||||
password: your_secret_password
|
|
||||||
certificate: /home/paulus/dev/addtrustexternalcaroot.crt
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
broker
|
|
||||||
*Required
|
|
||||||
This is the IP address of your MQTT broker, e.g. 192.168.1.32.
|
|
||||||
|
|
||||||
port
|
|
||||||
*Optional
|
|
||||||
The network port to connect to. Default is 1883.
|
|
||||||
|
|
||||||
client_id
|
|
||||||
*Optional
|
|
||||||
Client ID that Home Assistant will use. Has to be unique on the server.
|
|
||||||
Default is a random generated one.
|
|
||||||
|
|
||||||
keepalive
|
|
||||||
*Optional
|
|
||||||
The keep alive in seconds for this client. Default is 60.
|
|
||||||
|
|
||||||
certificate
|
|
||||||
*Optional
|
|
||||||
Certificate to use for encrypting the connection to the broker.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
@ -3,26 +3,8 @@ homeassistant.components.notify.file
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
File notification service.
|
File notification service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.file.html
|
||||||
To use the File notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: file
|
|
||||||
filename: FILENAME
|
|
||||||
timestamp: 1 or 0
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
filename
|
|
||||||
*Required
|
|
||||||
Name of the file to use. The file will be created if it doesn't exist and saved
|
|
||||||
in your config/ folder.
|
|
||||||
|
|
||||||
timestamp
|
|
||||||
*Required
|
|
||||||
Add a timestamp to the entry, valid entries are 1 or 0.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
@ -3,52 +3,8 @@ homeassistant.components.notify.instapush
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Instapush notification service.
|
Instapush notification service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.instapush.html
|
||||||
To use the Instapush notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: instapush
|
|
||||||
api_key: YOUR_APP_KEY
|
|
||||||
app_secret: YOUR_APP_SECRET
|
|
||||||
event: YOUR_EVENT
|
|
||||||
tracker: YOUR_TRACKER
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
To retrieve this value log into your account at https://instapush.im and go
|
|
||||||
to 'APPS', choose an app, and check 'Basic Info'.
|
|
||||||
|
|
||||||
app_secret
|
|
||||||
*Required
|
|
||||||
To get this value log into your account at https://instapush.im and go to
|
|
||||||
'APPS'. The 'Application ID' can be found under 'Basic Info'.
|
|
||||||
|
|
||||||
event
|
|
||||||
*Required
|
|
||||||
To retrieve a valid event log into your account at https://instapush.im and go
|
|
||||||
to 'APPS'. If you have no events to use with Home Assistant, create one event
|
|
||||||
for your app.
|
|
||||||
|
|
||||||
tracker
|
|
||||||
*Required
|
|
||||||
To retrieve the tracker value log into your account at https://instapush.im and
|
|
||||||
go to 'APPS', choose the app, and check the event entries.
|
|
||||||
|
|
||||||
Example usage of Instapush if you have an event 'notification' and a tracker
|
|
||||||
'home-assistant'.
|
|
||||||
|
|
||||||
curl -X POST \
|
|
||||||
-H "x-instapush-appid: YOUR_APP_KEY" \
|
|
||||||
-H "x-instapush-appsecret: YOUR_APP_SECRET" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"event":"notification","trackers":{"home-assistant":"Switch 1"}}' \
|
|
||||||
https://api.instapush.im/v1/post
|
|
||||||
|
|
||||||
Details for the API : https://instapush.im/developer/rest
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
@ -3,23 +3,8 @@ homeassistant.components.notify.nma
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
NMA (Notify My Android) notification service.
|
NMA (Notify My Android) notification service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.nma.html
|
||||||
To use the NMA notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: nma
|
|
||||||
api_key: YOUR_API_KEY
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
Enter the API key for NMA. Go to https://www.notifymyandroid.com and create a
|
|
||||||
new API key to use with Home Assistant.
|
|
||||||
|
|
||||||
Details for the API : https://www.notifymyandroid.com/api.jsp
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
@ -3,21 +3,8 @@ homeassistant.components.notify.pushbullet
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
PushBullet platform for notify component.
|
PushBullet platform for notify component.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.pushbullet.html
|
||||||
To use the PushBullet notifier you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: pushbullet
|
|
||||||
api_key: YOUR_API_KEY
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
Enter the API key for PushBullet. Go to https://www.pushbullet.com/ to retrieve
|
|
||||||
your API key.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -3,35 +3,8 @@ homeassistant.components.notify.pushover
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Pushover platform for notify component.
|
Pushover platform for notify component.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.pushover.html
|
||||||
To use the Pushover notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: pushover
|
|
||||||
api_key: ABCDEFGHJKLMNOPQRSTUVXYZ
|
|
||||||
user_key: ABCDEFGHJKLMNOPQRSTUVXYZ
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
This parameter is optional but should be configured, in order to get an API
|
|
||||||
key you should go to https://pushover.net and register a new application.
|
|
||||||
|
|
||||||
This is a quote from the pushover website regarding free/open source apps:
|
|
||||||
"If you are creating a client-side library, application, or open source project
|
|
||||||
that will be redistributed and installed by end-users, you may want to require
|
|
||||||
each of your users to register their own application rather than including your
|
|
||||||
own API token with the software."
|
|
||||||
|
|
||||||
When setting up the application I recommend using the icon located here:
|
|
||||||
https://home-assistant.io/images/favicon-192x192.png
|
|
||||||
|
|
||||||
user_key
|
|
||||||
*Required
|
|
||||||
To retrieve this value log into your account at https://pushover.net
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -3,27 +3,8 @@ homeassistant.components.notify.slack
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Slack platform for notify component.
|
Slack platform for notify component.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.slack.html
|
||||||
To use the Slack notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: slack
|
|
||||||
api_key: ABCDEFGHJKLMNOPQRSTUVXYZ
|
|
||||||
default_channel: '#general'
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
The slack API token to use for sending slack messages.
|
|
||||||
You can get your slack API token here https://api.slack.com/web?sudo=1
|
|
||||||
|
|
||||||
default_channel
|
|
||||||
*Required
|
|
||||||
The default channel to post to if no channel is explicitly specified when
|
|
||||||
sending the notification message.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -3,54 +3,8 @@ homeassistant.components.notify.smtp
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Mail (SMTP) notification service.
|
Mail (SMTP) notification service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.smtp.html
|
||||||
To use the smtp notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: smtp
|
|
||||||
server: MAIL_SERVER
|
|
||||||
port: YOUR_SMTP_PORT
|
|
||||||
sender: SENDER_EMAIL_ADDRESS
|
|
||||||
starttls: 1 or 0
|
|
||||||
username: YOUR_SMTP_USERNAME
|
|
||||||
password: YOUR_SMTP_PASSWORD
|
|
||||||
recipient: YOUR_RECIPIENT
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
server
|
|
||||||
*Required
|
|
||||||
SMTP server which is used to end the notifications. For Google Mail, eg.
|
|
||||||
smtp.gmail.com. Keep in mind that Google has some extra layers of protection
|
|
||||||
which need special attention (Hint: 'Less secure apps').
|
|
||||||
|
|
||||||
port
|
|
||||||
*Required
|
|
||||||
The port that the SMTP server is using, eg. 587 for Google Mail and STARTTLS
|
|
||||||
or 465/993 depending on your SMTP servers.
|
|
||||||
|
|
||||||
sender
|
|
||||||
*Required
|
|
||||||
E-Mail address of the sender.
|
|
||||||
|
|
||||||
starttls
|
|
||||||
*Optional
|
|
||||||
Enables STARTTLS, eg. 1 or 0.
|
|
||||||
|
|
||||||
username
|
|
||||||
*Required
|
|
||||||
Username for the SMTP account.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
Password for the SMTP server that belongs to the given username. If the
|
|
||||||
password contains a colon it need to be wrapped in apostrophes.
|
|
||||||
|
|
||||||
recipient
|
|
||||||
*Required
|
|
||||||
Recipient of the notification.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import smtplib
|
import smtplib
|
||||||
|
@ -3,31 +3,8 @@ homeassistant.components.notify.syslog
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Syslog notification service.
|
Syslog notification service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.syslog.html
|
||||||
To use the Syslog notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: syslog
|
|
||||||
facility: SYSLOG_FACILITY
|
|
||||||
option: SYSLOG_LOG_OPTION
|
|
||||||
priority: SYSLOG_PRIORITY
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
facility
|
|
||||||
*Optional
|
|
||||||
Facility according to RFC 3164 (http://tools.ietf.org/html/rfc3164). Default
|
|
||||||
is 'syslog' if no value is given.
|
|
||||||
|
|
||||||
option
|
|
||||||
*Option
|
|
||||||
Log option. Default is 'pid' if no value is given.
|
|
||||||
|
|
||||||
priority
|
|
||||||
*Optional
|
|
||||||
Priority of the messages. Default is 'info' if no value is given.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import syslog
|
import syslog
|
||||||
|
@ -3,31 +3,8 @@ homeassistant.components.notify.xmpp
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Jabber (XMPP) notification service.
|
Jabber (XMPP) notification service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/notify.xmpp.html
|
||||||
To use the Jabber notifier you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
notify:
|
|
||||||
platform: xmpp
|
|
||||||
sender: YOUR_JID
|
|
||||||
password: YOUR_JABBER_ACCOUNT_PASSWORD
|
|
||||||
recipient: YOUR_RECIPIENT
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
sender
|
|
||||||
*Required
|
|
||||||
The Jabber ID (JID) that will act as origin of the messages. Add your JID
|
|
||||||
including the domain, e.g. your_name@jabber.org.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Required
|
|
||||||
The password for your given Jabber account.
|
|
||||||
|
|
||||||
recipient
|
|
||||||
*Required
|
|
||||||
The Jabber ID (JID) that will receive the messages.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.recorder
|
homeassistant.components.recorder
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Component that records all events and state changes. Allows other components
|
||||||
|
to query this database.
|
||||||
|
|
||||||
Component that records all events and state changes.
|
For more details about this component, please refer to the documentation at
|
||||||
Allows other components to query this database.
|
https://home-assistant.io/components/recorder.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
@ -3,7 +3,7 @@ homeassistant.components.rfxtrx
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides support for RFXtrx components.
|
Provides support for RFXtrx components.
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/rfxtrx.html
|
https://home-assistant.io/components/rfxtrx.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
@ -1,39 +1,38 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.scene
|
homeassistant.components.scene
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Allows users to set and activate scenes.
|
||||||
|
|
||||||
Allows users to set and activate scenes within Home Assistant.
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/scene.html
|
||||||
A scene is a set of states that describe how you want certain entities to be.
|
|
||||||
For example, light A should be red with 100 brightness. Light B should be on.
|
|
||||||
|
|
||||||
A scene is active if all states of the scene match the real states.
|
|
||||||
|
|
||||||
If a scene is manually activated it will store the previous state of the
|
|
||||||
entities. These will be restored when the state is deactivated manually.
|
|
||||||
|
|
||||||
If one of the enties that are being tracked change state on its own, the
|
|
||||||
old state will not be restored when it is being deactivated.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
from homeassistant.helpers.event import track_state_change
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity import ToggleEntity
|
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.state import reproduce_state
|
from homeassistant.helpers.state import reproduce_state
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, STATE_OFF, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
ATTR_ENTITY_ID, STATE_OFF, STATE_ON, SERVICE_TURN_ON)
|
||||||
|
|
||||||
DOMAIN = 'scene'
|
DOMAIN = 'scene'
|
||||||
DEPENDENCIES = ['group']
|
DEPENDENCIES = ['group']
|
||||||
|
STATE = 'scening'
|
||||||
ATTR_ACTIVE_REQUESTED = "active_requested"
|
|
||||||
|
|
||||||
CONF_ENTITIES = "entities"
|
CONF_ENTITIES = "entities"
|
||||||
|
|
||||||
SceneConfig = namedtuple('SceneConfig', ['name', 'states', 'fuzzy_match'])
|
SceneConfig = namedtuple('SceneConfig', ['name', 'states'])
|
||||||
|
|
||||||
|
|
||||||
|
def activate(hass, entity_id=None):
|
||||||
|
""" Activate a scene. """
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
if entity_id:
|
||||||
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
|
|
||||||
|
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
@ -43,8 +42,9 @@ def setup(hass, config):
|
|||||||
|
|
||||||
scene_configs = config.get(DOMAIN)
|
scene_configs = config.get(DOMAIN)
|
||||||
|
|
||||||
if not isinstance(scene_configs, list):
|
if not isinstance(scene_configs, list) or \
|
||||||
logger.error('Scene config should be a list of scenes')
|
any(not isinstance(item, dict) for item in scene_configs):
|
||||||
|
logger.error('Scene config should be a list of dictionaries')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
component = EntityComponent(logger, DOMAIN, hass)
|
component = EntityComponent(logger, DOMAIN, hass)
|
||||||
@ -57,12 +57,8 @@ def setup(hass, config):
|
|||||||
target_scenes = component.extract_from_service(service)
|
target_scenes = component.extract_from_service(service)
|
||||||
|
|
||||||
for scene in target_scenes:
|
for scene in target_scenes:
|
||||||
if service.service == SERVICE_TURN_ON:
|
scene.activate()
|
||||||
scene.turn_on()
|
|
||||||
else:
|
|
||||||
scene.turn_off()
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_scene_service)
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service)
|
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -72,14 +68,6 @@ def _process_config(scene_config):
|
|||||||
""" Process passed in config into a format to work with. """
|
""" Process passed in config into a format to work with. """
|
||||||
name = scene_config.get('name')
|
name = scene_config.get('name')
|
||||||
|
|
||||||
fuzzy_match = scene_config.get('fuzzy_match')
|
|
||||||
if fuzzy_match:
|
|
||||||
# default to 1%
|
|
||||||
if isinstance(fuzzy_match, int):
|
|
||||||
fuzzy_match /= 100.0
|
|
||||||
else:
|
|
||||||
fuzzy_match = 0.01
|
|
||||||
|
|
||||||
states = {}
|
states = {}
|
||||||
c_entities = dict(scene_config.get(CONF_ENTITIES, {}))
|
c_entities = dict(scene_config.get(CONF_ENTITIES, {}))
|
||||||
|
|
||||||
@ -100,23 +88,16 @@ def _process_config(scene_config):
|
|||||||
|
|
||||||
states[entity_id.lower()] = State(entity_id, state, attributes)
|
states[entity_id.lower()] = State(entity_id, state, attributes)
|
||||||
|
|
||||||
return SceneConfig(name, states, fuzzy_match)
|
return SceneConfig(name, states)
|
||||||
|
|
||||||
|
|
||||||
class Scene(ToggleEntity):
|
class Scene(Entity):
|
||||||
""" A scene is a group of entities and the states we want them to be. """
|
""" A scene is a group of entities and the states we want them to be. """
|
||||||
|
|
||||||
def __init__(self, hass, scene_config):
|
def __init__(self, hass, scene_config):
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.scene_config = scene_config
|
self.scene_config = scene_config
|
||||||
|
|
||||||
self.is_active = False
|
|
||||||
self.prev_states = None
|
|
||||||
self.ignore_updates = False
|
|
||||||
|
|
||||||
track_state_change(
|
|
||||||
self.hass, self.entity_ids, self.entity_state_changed)
|
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -128,8 +109,8 @@ class Scene(ToggleEntity):
|
|||||||
return self.scene_config.name
|
return self.scene_config.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def state(self):
|
||||||
return self.is_active
|
return STATE
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def entity_ids(self):
|
def entity_ids(self):
|
||||||
@ -141,82 +122,8 @@ class Scene(ToggleEntity):
|
|||||||
""" Scene state attributes. """
|
""" Scene state attributes. """
|
||||||
return {
|
return {
|
||||||
ATTR_ENTITY_ID: list(self.entity_ids),
|
ATTR_ENTITY_ID: list(self.entity_ids),
|
||||||
ATTR_ACTIVE_REQUESTED: self.prev_states is not None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def turn_on(self):
|
def activate(self):
|
||||||
""" Activates scene. Tries to get entities into requested state. """
|
""" Activates scene. Tries to get entities into requested state. """
|
||||||
self.prev_states = tuple(self.hass.states.get(entity_id)
|
reproduce_state(self.hass, self.scene_config.states.values(), True)
|
||||||
for entity_id in self.entity_ids)
|
|
||||||
|
|
||||||
self._reproduce_state(self.scene_config.states.values())
|
|
||||||
|
|
||||||
def turn_off(self):
|
|
||||||
""" Deactivates scene and restores old states. """
|
|
||||||
if self.prev_states:
|
|
||||||
self._reproduce_state(self.prev_states)
|
|
||||||
self.prev_states = None
|
|
||||||
|
|
||||||
def entity_state_changed(self, entity_id, old_state, new_state):
|
|
||||||
""" Called when an entity part of this scene changes state. """
|
|
||||||
if self.ignore_updates:
|
|
||||||
return
|
|
||||||
|
|
||||||
# If new state is not what we expect, it can never be active
|
|
||||||
if self._state_as_requested(new_state):
|
|
||||||
self.update()
|
|
||||||
else:
|
|
||||||
self.is_active = False
|
|
||||||
self.prev_states = None
|
|
||||||
|
|
||||||
self.update_ha_state()
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
"""
|
|
||||||
Update if the scene is active.
|
|
||||||
|
|
||||||
Will look at each requested state and see if the current entity
|
|
||||||
has the same state and has at least the same attributes with the
|
|
||||||
same values. The real state can have more attributes.
|
|
||||||
"""
|
|
||||||
self.is_active = all(
|
|
||||||
self._state_as_requested(self.hass.states.get(entity_id))
|
|
||||||
for entity_id in self.entity_ids)
|
|
||||||
|
|
||||||
def _state_as_requested(self, cur_state):
|
|
||||||
""" Returns if given state is as requested. """
|
|
||||||
state = self.scene_config.states.get(cur_state and cur_state.entity_id)
|
|
||||||
|
|
||||||
return (cur_state is not None and state.state == cur_state.state and
|
|
||||||
all(self._compare_state_attribites(
|
|
||||||
value, cur_state.attributes.get(key))
|
|
||||||
for key, value in state.attributes.items()))
|
|
||||||
|
|
||||||
def _fuzzy_attribute_compare(self, attr_a, attr_b):
|
|
||||||
"""
|
|
||||||
Compare the attributes passed, use fuzzy logic if they are floats.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not (isinstance(attr_a, float) and isinstance(attr_b, float)):
|
|
||||||
return False
|
|
||||||
diff = abs(attr_a - attr_b) / (abs(attr_a) + abs(attr_b))
|
|
||||||
return diff <= self.scene_config.fuzzy_match
|
|
||||||
|
|
||||||
def _compare_state_attribites(self, attr1, attr2):
|
|
||||||
""" Compare the attributes passed, using fuzzy logic if specified. """
|
|
||||||
if attr1 == attr2:
|
|
||||||
return True
|
|
||||||
if not self.scene_config.fuzzy_match:
|
|
||||||
return False
|
|
||||||
if isinstance(attr1, list):
|
|
||||||
return all(self._fuzzy_attribute_compare(a, b)
|
|
||||||
for a, b in zip(attr1, attr2))
|
|
||||||
return self._fuzzy_attribute_compare(attr1, attr2)
|
|
||||||
|
|
||||||
def _reproduce_state(self, states):
|
|
||||||
""" Wraps reproduce state with Scence specific logic. """
|
|
||||||
self.ignore_updates = True
|
|
||||||
reproduce_state(self.hass, states, True)
|
|
||||||
self.ignore_updates = False
|
|
||||||
|
|
||||||
self.update_ha_state(True)
|
|
||||||
|
@ -1,161 +1,207 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.script
|
homeassistant.components.script
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
entity_id
|
|
||||||
Scripts are a sequence of actions that can be triggered manually
|
Scripts are a sequence of actions that can be triggered manually
|
||||||
by the user or automatically based upon automation events, etc.
|
by the user or automatically based upon automation events, etc.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/script.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import homeassistant.util.dt as date_util
|
import homeassistant.util.dt as date_util
|
||||||
|
from itertools import islice
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from homeassistant.helpers.event import track_point_in_time
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.helpers.entity import ToggleEntity
|
||||||
|
from homeassistant.helpers.event import track_point_in_utc_time
|
||||||
from homeassistant.util import split_entity_id
|
from homeassistant.util import split_entity_id
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF, EVENT_TIME_CHANGED)
|
ATTR_ENTITY_ID, EVENT_TIME_CHANGED, STATE_ON, SERVICE_TURN_ON,
|
||||||
|
SERVICE_TURN_OFF)
|
||||||
|
|
||||||
DOMAIN = "script"
|
DOMAIN = "script"
|
||||||
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
DEPENDENCIES = ["group"]
|
DEPENDENCIES = ["group"]
|
||||||
|
|
||||||
|
STATE_NOT_RUNNING = 'Not Running'
|
||||||
|
|
||||||
CONF_ALIAS = "alias"
|
CONF_ALIAS = "alias"
|
||||||
CONF_SERVICE = "execute_service"
|
CONF_SERVICE = "service"
|
||||||
|
CONF_SERVICE_OLD = "execute_service"
|
||||||
CONF_SERVICE_DATA = "service_data"
|
CONF_SERVICE_DATA = "service_data"
|
||||||
CONF_SEQUENCE = "sequence"
|
CONF_SEQUENCE = "sequence"
|
||||||
CONF_EVENT = "event"
|
CONF_EVENT = "event"
|
||||||
CONF_EVENT_DATA = "event_data"
|
CONF_EVENT_DATA = "event_data"
|
||||||
CONF_DELAY = "delay"
|
CONF_DELAY = "delay"
|
||||||
ATTR_ENTITY_ID = "entity_id"
|
|
||||||
|
ATTR_LAST_ACTION = 'last_action'
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def is_on(hass, entity_id):
|
||||||
|
""" Returns if the switch is on based on the statemachine. """
|
||||||
|
return hass.states.is_state(entity_id, STATE_ON)
|
||||||
|
|
||||||
|
|
||||||
|
def turn_on(hass, entity_id):
|
||||||
|
""" Turn script on. """
|
||||||
|
_, object_id = split_entity_id(entity_id)
|
||||||
|
|
||||||
|
hass.services.call(DOMAIN, object_id)
|
||||||
|
|
||||||
|
|
||||||
|
def turn_off(hass, entity_id):
|
||||||
|
""" Turn script on. """
|
||||||
|
hass.services.call(DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id})
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Load the scripts from the configuration. """
|
""" Load the scripts from the configuration. """
|
||||||
|
|
||||||
scripts = []
|
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||||
|
|
||||||
|
def service_handler(service):
|
||||||
|
""" Execute a service call to script.<script name>. """
|
||||||
|
entity_id = ENTITY_ID_FORMAT.format(service.service)
|
||||||
|
script = component.entities.get(entity_id)
|
||||||
|
if script:
|
||||||
|
script.turn_on()
|
||||||
|
|
||||||
for name, cfg in config[DOMAIN].items():
|
for name, cfg in config[DOMAIN].items():
|
||||||
if CONF_SEQUENCE not in cfg:
|
if not cfg.get(CONF_SEQUENCE):
|
||||||
_LOGGER.warn("Missing key 'sequence' for script %s", name)
|
_LOGGER.warn("Missing key 'sequence' for script %s", name)
|
||||||
continue
|
continue
|
||||||
alias = cfg.get(CONF_ALIAS, name)
|
alias = cfg.get(CONF_ALIAS, name)
|
||||||
entity_id = "{}.{}".format(DOMAIN, name)
|
script = Script(hass, alias, cfg[CONF_SEQUENCE])
|
||||||
script = Script(hass, entity_id, alias, cfg[CONF_SEQUENCE])
|
component.add_entities((script,))
|
||||||
hass.services.register(DOMAIN, name, script)
|
_, object_id = split_entity_id(script.entity_id)
|
||||||
scripts.append(script)
|
hass.services.register(DOMAIN, object_id, service_handler)
|
||||||
|
|
||||||
def _get_entities(service):
|
def turn_on_service(service):
|
||||||
""" Make sure that we always get a list of entities """
|
""" Calls a service to turn script on. """
|
||||||
if isinstance(service.data[ATTR_ENTITY_ID], list):
|
# We could turn on script directly here, but we only want to offer
|
||||||
return service.data[ATTR_ENTITY_ID]
|
# one way to do it. Otherwise no easy way to call invocations.
|
||||||
else:
|
for script in component.extract_from_service(service):
|
||||||
return [service.data[ATTR_ENTITY_ID]]
|
turn_on(hass, script.entity_id)
|
||||||
|
|
||||||
def turn_on(service):
|
def turn_off_service(service):
|
||||||
""" Calls a script. """
|
|
||||||
for entity_id in _get_entities(service):
|
|
||||||
domain, service = split_entity_id(entity_id)
|
|
||||||
hass.services.call(domain, service, {})
|
|
||||||
|
|
||||||
def turn_off(service):
|
|
||||||
""" Cancels a script. """
|
""" Cancels a script. """
|
||||||
for entity_id in _get_entities(service):
|
for script in component.extract_from_service(service):
|
||||||
for script in scripts:
|
script.turn_off()
|
||||||
if script.entity_id == entity_id:
|
|
||||||
script.cancel()
|
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on)
|
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on_service)
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off)
|
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off_service)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Script(object):
|
class Script(ToggleEntity):
|
||||||
# pylint: disable=attribute-defined-outside-init
|
""" Represents a script. """
|
||||||
# pylint: disable=too-many-instance-attributes
|
def __init__(self, hass, name, sequence):
|
||||||
# pylint: disable=too-few-public-methods
|
|
||||||
"""
|
|
||||||
A script contains a sequence of service calls or configured delays
|
|
||||||
that are executed in order.
|
|
||||||
Each script also has a state (on/off) indicating whether the script is
|
|
||||||
running or not.
|
|
||||||
"""
|
|
||||||
def __init__(self, hass, entity_id, alias, sequence):
|
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.alias = alias
|
self._name = name
|
||||||
self.sequence = sequence
|
self.sequence = sequence
|
||||||
self.entity_id = entity_id
|
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
self._reset()
|
self._cur = -1
|
||||||
|
self._last_action = None
|
||||||
|
self._listener = None
|
||||||
|
|
||||||
def cancel(self):
|
@property
|
||||||
""" Cancels a running script and resets the state back to off. """
|
def should_poll(self):
|
||||||
_LOGGER.info("Cancelled script %s", self.alias)
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the entity. """
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
""" Returns the state attributes. """
|
||||||
|
attrs = {}
|
||||||
|
|
||||||
|
if self._last_action:
|
||||||
|
attrs[ATTR_LAST_ACTION] = self._last_action
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
""" True if entity is on. """
|
||||||
|
return self._cur != -1
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
""" Turn the entity on. """
|
||||||
|
_LOGGER.info("Executing script %s", self._name)
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if self.listener:
|
if self._cur == -1:
|
||||||
self.hass.bus.remove_listener(EVENT_TIME_CHANGED,
|
self._cur = 0
|
||||||
self.listener)
|
|
||||||
self.listener = None
|
|
||||||
self._reset()
|
|
||||||
|
|
||||||
def _reset(self):
|
# Unregister callback if we were in a delay but turn on is called
|
||||||
""" Resets a script back to default state so that it is ready to
|
# again. In that case we just continue execution.
|
||||||
run from the start again. """
|
self._remove_listener()
|
||||||
self.actions = None
|
|
||||||
self.listener = None
|
|
||||||
self.last_action = "Not Running"
|
|
||||||
self.hass.states.set(self.entity_id, STATE_OFF, {
|
|
||||||
"friendly_name": self.alias,
|
|
||||||
"last_action": self.last_action
|
|
||||||
})
|
|
||||||
|
|
||||||
def _execute_until_done(self):
|
for cur, action in islice(enumerate(self.sequence), self._cur,
|
||||||
""" Executes a sequence of actions until finished or until a delay
|
None):
|
||||||
is encountered. If a delay action is encountered, the script
|
|
||||||
registers itself to be called again in the future, when
|
|
||||||
_execute_until_done will resume.
|
|
||||||
|
|
||||||
Returns True if finished, False otherwise. """
|
if CONF_SERVICE in action or CONF_SERVICE_OLD in action:
|
||||||
for action in self.actions:
|
self._call_service(action)
|
||||||
if CONF_SERVICE in action:
|
|
||||||
self._call_service(action)
|
|
||||||
elif CONF_EVENT in action:
|
|
||||||
self._fire_event(action)
|
|
||||||
elif CONF_DELAY in action:
|
|
||||||
delay = timedelta(**action[CONF_DELAY])
|
|
||||||
point_in_time = date_util.now() + delay
|
|
||||||
self.listener = track_point_in_time(
|
|
||||||
self.hass, self, point_in_time)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
elif CONF_EVENT in action:
|
||||||
""" Executes the script. """
|
self._fire_event(action)
|
||||||
_LOGGER.info("Executing script %s", self.alias)
|
|
||||||
|
elif CONF_DELAY in action:
|
||||||
|
# Call ourselves in the future to continue work
|
||||||
|
def script_delay(now):
|
||||||
|
""" Called after delay is done. """
|
||||||
|
self._listener = None
|
||||||
|
self.turn_on()
|
||||||
|
|
||||||
|
delay = timedelta(**action[CONF_DELAY])
|
||||||
|
self._listener = track_point_in_utc_time(
|
||||||
|
self.hass, script_delay, date_util.utcnow() + delay)
|
||||||
|
self._cur = cur + 1
|
||||||
|
self.update_ha_state()
|
||||||
|
return
|
||||||
|
|
||||||
|
self._cur = -1
|
||||||
|
self._last_action = None
|
||||||
|
self.update_ha_state()
|
||||||
|
|
||||||
|
def turn_off(self, **kwargs):
|
||||||
|
""" Turn script off. """
|
||||||
|
_LOGGER.info("Cancelled script %s", self._name)
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if self.actions is None:
|
if self._cur == -1:
|
||||||
self.actions = (action for action in self.sequence)
|
return
|
||||||
|
|
||||||
if not self._execute_until_done():
|
self._cur = -1
|
||||||
state = self.hass.states.get(self.entity_id)
|
self.update_ha_state()
|
||||||
state.attributes['last_action'] = self.last_action
|
self._remove_listener()
|
||||||
self.hass.states.set(self.entity_id, STATE_ON,
|
|
||||||
state.attributes)
|
|
||||||
else:
|
|
||||||
self._reset()
|
|
||||||
|
|
||||||
def _call_service(self, action):
|
def _call_service(self, action):
|
||||||
""" Calls the service specified in the action. """
|
""" Calls the service specified in the action. """
|
||||||
self.last_action = action.get(CONF_ALIAS, action[CONF_SERVICE])
|
conf_service = action.get(CONF_SERVICE, action.get(CONF_SERVICE_OLD))
|
||||||
_LOGGER.info("Executing script %s step %s", self.alias,
|
self._last_action = action.get(CONF_ALIAS, conf_service)
|
||||||
self.last_action)
|
_LOGGER.info("Executing script %s step %s", self._name,
|
||||||
domain, service = split_entity_id(action[CONF_SERVICE])
|
self._last_action)
|
||||||
|
domain, service = split_entity_id(conf_service)
|
||||||
data = action.get(CONF_SERVICE_DATA, {})
|
data = action.get(CONF_SERVICE_DATA, {})
|
||||||
self.hass.services.call(domain, service, data)
|
self.hass.services.call(domain, service, data)
|
||||||
|
|
||||||
def _fire_event(self, action):
|
def _fire_event(self, action):
|
||||||
""" Fires an event. """
|
""" Fires an event. """
|
||||||
self.last_action = action.get(CONF_ALIAS, action[CONF_EVENT])
|
self._last_action = action.get(CONF_ALIAS, action[CONF_EVENT])
|
||||||
_LOGGER.info("Executing script %s step %s", self.alias,
|
_LOGGER.info("Executing script %s step %s", self._name,
|
||||||
self.last_action)
|
self._last_action)
|
||||||
self.hass.bus.fire(action[CONF_EVENT], action.get(CONF_EVENT_DATA))
|
self.hass.bus.fire(action[CONF_EVENT], action.get(CONF_EVENT_DATA))
|
||||||
|
|
||||||
|
def _remove_listener(self):
|
||||||
|
""" Remove point in time listener, if any. """
|
||||||
|
if self._listener:
|
||||||
|
self.hass.bus.remove_listener(EVENT_TIME_CHANGED,
|
||||||
|
self._listener)
|
||||||
|
self._listener = None
|
||||||
|
@ -4,36 +4,8 @@ homeassistant.components.sensor.arduino
|
|||||||
Support for getting information from Arduino pins. Only analog pins are
|
Support for getting information from Arduino pins. Only analog pins are
|
||||||
supported.
|
supported.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.arduino.html
|
||||||
To use the arduino sensor you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: arduino
|
|
||||||
pins:
|
|
||||||
7:
|
|
||||||
name: Door switch
|
|
||||||
type: analog
|
|
||||||
0:
|
|
||||||
name: Brightness
|
|
||||||
type: analog
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
pins
|
|
||||||
*Required
|
|
||||||
An array specifying the digital pins to use on the Arduino board.
|
|
||||||
|
|
||||||
These are the variables for the pins array:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Required
|
|
||||||
The name for the pin that will be used in the frontend.
|
|
||||||
|
|
||||||
type
|
|
||||||
*Required
|
|
||||||
The type of the pin: 'analog'.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -12,11 +12,12 @@ from datetime import timedelta
|
|||||||
|
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.const import DEVICE_DEFAULT_NAME
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
|
||||||
|
|
||||||
CONF_RESOURCE = 'resource'
|
CONF_RESOURCE = 'resource'
|
||||||
CONF_MONITORED_VARIABLES = 'monitored_variables'
|
CONF_MONITORED_VARIABLES = 'monitored_variables'
|
||||||
@ -40,35 +41,68 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
"Add http:// to your URL.")
|
"Add http:// to your URL.")
|
||||||
return False
|
return False
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
_LOGGER.error("No route to device. "
|
_LOGGER.error("No route to device at %s. "
|
||||||
"Please check the IP address in the configuration file.")
|
"Please check the IP address in the configuration file.",
|
||||||
|
resource)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
arest = ArestData(resource)
|
arest = ArestData(resource)
|
||||||
|
|
||||||
dev = []
|
dev = []
|
||||||
|
pins = config.get('pins', None)
|
||||||
|
|
||||||
for variable in config['monitored_variables']:
|
for variable in config['monitored_variables']:
|
||||||
if variable['name'] not in response['variables']:
|
if variable['name'] not in response['variables']:
|
||||||
_LOGGER.error('Variable: "%s" does not exist', variable['name'])
|
_LOGGER.error('Variable: "%s" does not exist', variable['name'])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
dev.append(ArestSensor(arest, response['name'], variable['name'],
|
dev.append(ArestSensor(arest,
|
||||||
variable.get('unit')))
|
resource,
|
||||||
|
config.get('name', response['name']),
|
||||||
|
variable['name'],
|
||||||
|
variable=variable['name'],
|
||||||
|
unit_of_measurement=variable.get(
|
||||||
|
'unit_of_measurement')))
|
||||||
|
|
||||||
|
for pinnum, pin in pins.items():
|
||||||
|
dev.append(ArestSensor(ArestData(resource, pinnum),
|
||||||
|
resource,
|
||||||
|
config.get('name', response['name']),
|
||||||
|
pin.get('name'),
|
||||||
|
pin=pinnum,
|
||||||
|
unit_of_measurement=pin.get(
|
||||||
|
'unit_of_measurement'),
|
||||||
|
corr_factor=pin.get('correction_factor', None),
|
||||||
|
decimal_places=pin.get('decimal_places', None)))
|
||||||
|
|
||||||
add_devices(dev)
|
add_devices(dev)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-instance-attributes, too-many-arguments
|
||||||
class ArestSensor(Entity):
|
class ArestSensor(Entity):
|
||||||
""" Implements an aREST sensor. """
|
""" Implements an aREST sensor for exposed variables. """
|
||||||
|
|
||||||
def __init__(self, arest, location, variable, unit_of_measurement):
|
def __init__(self, arest, resource, location, name, variable=None,
|
||||||
|
pin=None, unit_of_measurement=None, corr_factor=None,
|
||||||
|
decimal_places=None):
|
||||||
self.arest = arest
|
self.arest = arest
|
||||||
self._name = '{} {}'.format(location.title(), variable.title())
|
self._resource = resource
|
||||||
|
self._name = '{} {}'.format(location.title(), name.title()) \
|
||||||
|
or DEVICE_DEFAULT_NAME
|
||||||
self._variable = variable
|
self._variable = variable
|
||||||
|
self._pin = pin
|
||||||
self._state = 'n/a'
|
self._state = 'n/a'
|
||||||
self._unit_of_measurement = unit_of_measurement
|
self._unit_of_measurement = unit_of_measurement
|
||||||
|
self._corr_factor = corr_factor
|
||||||
|
self._decimal_places = decimal_places
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
if self._pin is not None:
|
||||||
|
request = requests.get('{}/mode/{}/i'.format
|
||||||
|
(self._resource, self._pin), timeout=10)
|
||||||
|
if request.status_code is not 200:
|
||||||
|
_LOGGER.error("Can't set mode. Is device offline?")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
""" The name of the sensor. """
|
""" The name of the sensor. """
|
||||||
@ -86,6 +120,16 @@ class ArestSensor(Entity):
|
|||||||
|
|
||||||
if 'error' in values:
|
if 'error' in values:
|
||||||
return values['error']
|
return values['error']
|
||||||
|
elif 'value' in values:
|
||||||
|
if self._corr_factor is not None \
|
||||||
|
and self._decimal_places is not None:
|
||||||
|
return round((float(values['value']) *
|
||||||
|
float(self._corr_factor)), self._decimal_places)
|
||||||
|
elif self._corr_factor is not None \
|
||||||
|
and self._decimal_places is None:
|
||||||
|
return round(float(values['value']) * float(self._corr_factor))
|
||||||
|
else:
|
||||||
|
return values['value']
|
||||||
else:
|
else:
|
||||||
return values.get(self._variable, 'n/a')
|
return values.get(self._variable, 'n/a')
|
||||||
|
|
||||||
@ -96,18 +140,34 @@ class ArestSensor(Entity):
|
|||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
class ArestData(object):
|
class ArestData(object):
|
||||||
""" Class for handling the data retrieval. """
|
""" Class for handling the data retrieval for variables. """
|
||||||
|
|
||||||
def __init__(self, resource):
|
def __init__(self, resource, pin=None):
|
||||||
self.resource = resource
|
self._resource = resource
|
||||||
|
self._pin = pin
|
||||||
self.data = {}
|
self.data = {}
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Gets the latest data from aREST device. """
|
""" Gets the latest data from aREST device. """
|
||||||
try:
|
try:
|
||||||
response = requests.get(self.resource, timeout=10)
|
if self._pin is None:
|
||||||
self.data = response.json()['variables']
|
response = requests.get(self._resource, timeout=10)
|
||||||
|
self.data = response.json()['variables']
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if str(self._pin[0]) == 'A':
|
||||||
|
response = requests.get('{}/analog/{}'.format(
|
||||||
|
self._resource, self._pin[1:]), timeout=10)
|
||||||
|
self.data = {'value': response.json()['return_value']}
|
||||||
|
else:
|
||||||
|
_LOGGER.error("Wrong pin naming. "
|
||||||
|
"Please check your configuration file.")
|
||||||
|
except TypeError:
|
||||||
|
response = requests.get('{}/digital/{}'.format(
|
||||||
|
self._resource, self._pin), timeout=10)
|
||||||
|
self.data = {'value': response.json()['return_value']}
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
_LOGGER.error("No route to device. Is device offline?")
|
_LOGGER.error("No route to device %s. Is device offline?",
|
||||||
|
self._resource)
|
||||||
self.data = {'error': 'error fetching'}
|
self.data = {'error': 'error fetching'}
|
||||||
|
@ -3,65 +3,8 @@ homeassistant.components.sensor.bitcoin
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Bitcoin information service that uses blockchain.info and its online wallet.
|
Bitcoin information service that uses blockchain.info and its online wallet.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.bitcoin.html
|
||||||
You need to enable the API access for your online wallet to get the balance.
|
|
||||||
To do that log in and move to 'Account Setting', choose 'IP Restrictions', and
|
|
||||||
check 'Enable Api Access'. You will get an email message from blockchain.info
|
|
||||||
where you must authorize the API access.
|
|
||||||
|
|
||||||
To use the Bitcoin sensor you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: bitcoin
|
|
||||||
wallet: 'YOUR WALLET_ID'
|
|
||||||
password: YOUR_ACCOUNT_PASSWORD
|
|
||||||
currency: YOUR CURRENCY
|
|
||||||
display_options:
|
|
||||||
- exchangerate
|
|
||||||
- trade_volume_btc
|
|
||||||
- miners_revenue_usd
|
|
||||||
- btc_mined
|
|
||||||
- trade_volume_usd
|
|
||||||
- difficulty
|
|
||||||
- minutes_between_blocks
|
|
||||||
- number_of_transactions
|
|
||||||
- hash_rate
|
|
||||||
- timestamp
|
|
||||||
- mined_blocks
|
|
||||||
- blocks_size
|
|
||||||
- total_fees_btc
|
|
||||||
- total_btc_sent
|
|
||||||
- estimated_btc_sent
|
|
||||||
- total_btc
|
|
||||||
- total_blocks
|
|
||||||
- next_retarget
|
|
||||||
- estimated_transaction_volume_usd
|
|
||||||
- miners_revenue_btc
|
|
||||||
- market_price_usd
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
wallet
|
|
||||||
*Optional
|
|
||||||
This is your wallet identifier from https://blockchain.info to access the
|
|
||||||
online wallet.
|
|
||||||
|
|
||||||
password
|
|
||||||
*Optional
|
|
||||||
Password your your online wallet.
|
|
||||||
|
|
||||||
currency
|
|
||||||
*Optional
|
|
||||||
The currency to exchange to, eg. CHF, USD, EUR,etc. Default is USD.
|
|
||||||
|
|
||||||
display_options
|
|
||||||
*Optional
|
|
||||||
An array specifying the variables to display.
|
|
||||||
|
|
||||||
These are the variables for the display_options array. See the configuration
|
|
||||||
example above for a list of all available variables.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -3,41 +3,6 @@ homeassistant.components.sensor.command_sensor
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Allows to configure custom shell commands to turn a value for a sensor.
|
Allows to configure custom shell commands to turn a value for a sensor.
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the command_line sensor you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: command_sensor
|
|
||||||
name: "Command sensor"
|
|
||||||
command: sensor_command
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
correction_factor: 0.0001
|
|
||||||
decimal_places: 0
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
Name of the command sensor.
|
|
||||||
|
|
||||||
command
|
|
||||||
*Required
|
|
||||||
The action to take to get the value.
|
|
||||||
|
|
||||||
unit_of_measurement
|
|
||||||
*Optional
|
|
||||||
Defines the units of measurement of the sensor, if any.
|
|
||||||
|
|
||||||
correction_factor
|
|
||||||
*Optional
|
|
||||||
A float value to do some basic calculations.
|
|
||||||
|
|
||||||
decimal_places
|
|
||||||
*Optional
|
|
||||||
Number of decimal places of the value. Default is 0.
|
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/sensor.command_sensor.html
|
https://home-assistant.io/components/sensor.command_sensor.html
|
||||||
"""
|
"""
|
||||||
@ -133,7 +98,7 @@ class CommandSensorData(object):
|
|||||||
_LOGGER.info('Running command: %s', self.command)
|
_LOGGER.info('Running command: %s', self.command)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return_value = subprocess.check_output(self.command.split())
|
return_value = subprocess.check_output(self.command, shell=True)
|
||||||
self.value = return_value.strip().decode('utf-8')
|
self.value = return_value.strip().decode('utf-8')
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
_LOGGER.error('Command failed: %s', self.command)
|
_LOGGER.error('Command failed: %s', self.command)
|
||||||
|
76
homeassistant/components/sensor/cpuspeed.py
Normal file
76
homeassistant/components/sensor/cpuspeed.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.sensor.cpuspeed
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Shows the current CPU speed.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.cpuspeed.html
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
REQUIREMENTS = ['py-cpuinfo==0.1.6']
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEFAULT_NAME = "CPU speed"
|
||||||
|
|
||||||
|
ATTR_VENDOR = 'Vendor ID'
|
||||||
|
ATTR_BRAND = 'Brand'
|
||||||
|
ATTR_HZ = 'GHz Advertised'
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-variable
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up the CPU speed sensor. """
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cpuinfo # noqa
|
||||||
|
except ImportError:
|
||||||
|
_LOGGER.exception(
|
||||||
|
"Unable to import cpuinfo. "
|
||||||
|
"Did you maybe not install the 'py-cpuinfo' package?")
|
||||||
|
return False
|
||||||
|
|
||||||
|
add_devices([CpuSpeedSensor(config.get('name', DEFAULT_NAME))])
|
||||||
|
|
||||||
|
|
||||||
|
class CpuSpeedSensor(Entity):
|
||||||
|
""" A CPU info sensor. """
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self._name = name
|
||||||
|
self._state = None
|
||||||
|
self._unit_of_measurement = 'GHz'
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the state of the device. """
|
||||||
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
""" Returns the state attributes. """
|
||||||
|
if self.info is not None:
|
||||||
|
return {
|
||||||
|
ATTR_VENDOR: self.info['vendor_id'],
|
||||||
|
ATTR_BRAND: self.info['brand'],
|
||||||
|
ATTR_HZ: round(self.info['hz_advertised_raw'][0]/10**9, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
""" Gets the latest data and updates the state. """
|
||||||
|
from cpuinfo import cpuinfo
|
||||||
|
|
||||||
|
self.info = cpuinfo.get_cpu_info()
|
||||||
|
self._state = round(float(self.info['hz_actual_raw'][0])/10**9, 2)
|
@ -2,39 +2,9 @@
|
|||||||
homeassistant.components.sensor.dht
|
homeassistant.components.sensor.dht
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Adafruit DHT temperature and humidity sensor.
|
Adafruit DHT temperature and humidity sensor.
|
||||||
You need a Python3 compatible version of the Adafruit_Python_DHT library
|
|
||||||
(e.g. https://github.com/mala-zaba/Adafruit_Python_DHT,
|
|
||||||
also see requirements.txt).
|
|
||||||
As this requires access to the GPIO, you will need to run home-assistant
|
|
||||||
as root.
|
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.dht.html
|
||||||
To use the Adafruit DHT sensor you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: dht
|
|
||||||
sensor: DHT22
|
|
||||||
pin: 23
|
|
||||||
monitored_conditions:
|
|
||||||
- temperature
|
|
||||||
- humidity
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
sensor
|
|
||||||
*Required
|
|
||||||
The sensor type, DHT11, DHT22 or AM2302
|
|
||||||
|
|
||||||
pin
|
|
||||||
*Required
|
|
||||||
The pin the sensor is connected to, something like
|
|
||||||
'P8_11' for Beaglebone, '23' for Raspberry Pi
|
|
||||||
|
|
||||||
monitored_conditions
|
|
||||||
*Optional
|
|
||||||
Conditions to monitor. Available conditions are temperature and humidity.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -4,47 +4,8 @@ homeassistant.components.sensor.efergy
|
|||||||
Monitors home energy use as measured by an efergy engage hub using its
|
Monitors home energy use as measured by an efergy engage hub using its
|
||||||
(unofficial, undocumented) API.
|
(unofficial, undocumented) API.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.efergy.html
|
||||||
To use the efergy sensor you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: efergy
|
|
||||||
app_token: APP_TOKEN
|
|
||||||
utc_offset: UTC_OFFSET
|
|
||||||
monitored_variables:
|
|
||||||
- type: instant_readings
|
|
||||||
- type: budget
|
|
||||||
- type: cost
|
|
||||||
period: day
|
|
||||||
currency: $
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
To get a new App Token, log in to your efergy account, go
|
|
||||||
to the Settings page, click on App tokens, and click "Add token".
|
|
||||||
|
|
||||||
utc_offset
|
|
||||||
*Required for some variables
|
|
||||||
Some variables (currently only the daily_cost) require that the
|
|
||||||
negative number of minutes your timezone is ahead/behind UTC time.
|
|
||||||
|
|
||||||
monitored_variables
|
|
||||||
*Required
|
|
||||||
An array specifying the variables to monitor.
|
|
||||||
|
|
||||||
period
|
|
||||||
*Optional
|
|
||||||
Some variables take a period argument. Valid options are "day", "week",
|
|
||||||
"month", and "year".
|
|
||||||
|
|
||||||
currency
|
|
||||||
*Optional
|
|
||||||
This is used to display the cost/period as the unit when monitoring the
|
|
||||||
cost. It should correspond to the actual currency used in your dashboard.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from requests import get
|
from requests import get
|
||||||
|
@ -3,46 +3,8 @@ homeassistant.components.sensor.forecast
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Forecast.io weather service.
|
Forecast.io weather service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.forecast.html
|
||||||
To use the Forecast sensor you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: forecast
|
|
||||||
api_key: YOUR_APP_KEY
|
|
||||||
monitored_conditions:
|
|
||||||
- summary
|
|
||||||
- precip_type
|
|
||||||
- precip_intensity
|
|
||||||
- temperature
|
|
||||||
- dew_point
|
|
||||||
- wind_speed
|
|
||||||
- wind_bearing
|
|
||||||
- cloud_cover
|
|
||||||
- humidity
|
|
||||||
- pressure
|
|
||||||
- visibility
|
|
||||||
- ozone
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
To retrieve this value log into your account at http://forecast.io/. You can
|
|
||||||
make 1000 requests per day. This means that you could create every 1.4 minute
|
|
||||||
one.
|
|
||||||
|
|
||||||
monitored_conditions
|
|
||||||
*Required
|
|
||||||
An array specifying the conditions to monitor.
|
|
||||||
|
|
||||||
monitored_conditions
|
|
||||||
*Required
|
|
||||||
Conditions to monitor. See the configuration example above for a
|
|
||||||
list of all available conditions to monitor.
|
|
||||||
|
|
||||||
Details for the API : https://developer.forecast.io/docs/v2
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
@ -55,23 +17,34 @@ except ImportError:
|
|||||||
forecastio = None
|
forecastio = None
|
||||||
|
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS, TEMP_FAHRENHEIT)
|
from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS)
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Sensor types are defined like so:
|
||||||
|
# Name, si unit, us unit, ca unit, uk unit, uk2 unit
|
||||||
SENSOR_TYPES = {
|
SENSOR_TYPES = {
|
||||||
'summary': ['Summary', ''],
|
'summary': ['Summary', '', '', '', '', ''],
|
||||||
'precip_type': ['Precip', ''],
|
'icon': ['Icon', '', '', '', '', ''],
|
||||||
'precip_intensity': ['Precip intensity', 'mm'],
|
'nearest_storm_distance': ['Nearest Storm Distance',
|
||||||
'temperature': ['Temperature', ''],
|
'km', 'm', 'km', 'km', 'm'],
|
||||||
'dew_point': ['Dew point', '°C'],
|
'nearest_storm_bearing': ['Nearest Storm Bearing',
|
||||||
'wind_speed': ['Wind Speed', 'm/s'],
|
'°', '°', '°', '°', '°'],
|
||||||
'wind_bearing': ['Wind Bearing', '°'],
|
'precip_type': ['Precip', '', '', '', '', ''],
|
||||||
'cloud_cover': ['Cloud coverage', '%'],
|
'precip_intensity': ['Precip Intensity', 'mm', 'in', 'mm', 'mm', 'mm'],
|
||||||
'humidity': ['Humidity', '%'],
|
'precip_probability': ['Precip Probability', '%', '%', '%', '%', '%'],
|
||||||
'pressure': ['Pressure', 'mBar'],
|
'temperature': ['Temperature', '°C', '°F', '°C', '°C', '°C'],
|
||||||
'visibility': ['Visibility', 'km'],
|
'apparent_temperature': ['Apparent Temperature',
|
||||||
'ozone': ['Ozone', 'DU'],
|
'°C', '°F', '°C', '°C', '°C'],
|
||||||
|
'dew_point': ['Dew point', '°C', '°F', '°C', '°C', '°C'],
|
||||||
|
'wind_speed': ['Wind Speed', 'm/s', 'mph', 'km/h', 'mph', 'mph'],
|
||||||
|
'wind_bearing': ['Wind Bearing', '°', '°', '°', '°', '°'],
|
||||||
|
'cloud_cover': ['Cloud Coverage', '%', '%', '%', '%', '%'],
|
||||||
|
'humidity': ['Humidity', '%', '%', '%', '%', '%'],
|
||||||
|
'pressure': ['Pressure', 'mBar', 'mBar', 'mBar', 'mBar', 'mBar'],
|
||||||
|
'visibility': ['Visibility', 'km', 'm', 'km', 'km', 'm'],
|
||||||
|
'ozone': ['Ozone', 'DU', 'DU', 'DU', 'DU', 'DU'],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
@ -90,9 +63,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
|
|
||||||
unit = hass.config.temperature_unit
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
forecast = forecastio.load_forecast(config.get(CONF_API_KEY, None),
|
forecast = forecastio.load_forecast(config.get(CONF_API_KEY, None),
|
||||||
hass.config.latitude,
|
hass.config.latitude,
|
||||||
@ -104,16 +74,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
"Please check your settings for Forecast.io.")
|
"Please check your settings for Forecast.io.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if 'units' in config:
|
||||||
|
units = config['units']
|
||||||
|
elif hass.config.temperature_unit == TEMP_CELCIUS:
|
||||||
|
units = 'si'
|
||||||
|
else:
|
||||||
|
units = 'us'
|
||||||
|
|
||||||
data = ForeCastData(config.get(CONF_API_KEY, None),
|
data = ForeCastData(config.get(CONF_API_KEY, None),
|
||||||
hass.config.latitude,
|
hass.config.latitude,
|
||||||
hass.config.longitude)
|
hass.config.longitude,
|
||||||
|
units)
|
||||||
|
|
||||||
dev = []
|
dev = []
|
||||||
for variable in config['monitored_conditions']:
|
for variable in config['monitored_conditions']:
|
||||||
if variable not in SENSOR_TYPES:
|
if variable not in SENSOR_TYPES:
|
||||||
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
||||||
else:
|
else:
|
||||||
dev.append(ForeCastSensor(data, variable, unit))
|
dev.append(ForeCastSensor(data, variable))
|
||||||
|
|
||||||
add_devices(dev)
|
add_devices(dev)
|
||||||
|
|
||||||
@ -122,14 +100,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
class ForeCastSensor(Entity):
|
class ForeCastSensor(Entity):
|
||||||
""" Implements an Forecast.io sensor. """
|
""" Implements an Forecast.io sensor. """
|
||||||
|
|
||||||
def __init__(self, weather_data, sensor_type, unit):
|
def __init__(self, weather_data, sensor_type):
|
||||||
self.client_name = 'Weather'
|
self.client_name = 'Weather'
|
||||||
self._name = SENSOR_TYPES[sensor_type][0]
|
self._name = SENSOR_TYPES[sensor_type][0]
|
||||||
self.forecast_client = weather_data
|
self.forecast_client = weather_data
|
||||||
self._unit = unit
|
|
||||||
self.type = sensor_type
|
self.type = sensor_type
|
||||||
self._state = None
|
self._state = None
|
||||||
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
self._unit_system = self.forecast_client.unit_system
|
||||||
|
if self._unit_system == 'si':
|
||||||
|
self._unit_of_measurement = SENSOR_TYPES[self.type][1]
|
||||||
|
elif self._unit_system == 'us':
|
||||||
|
self._unit_of_measurement = SENSOR_TYPES[self.type][2]
|
||||||
|
elif self._unit_system == 'ca':
|
||||||
|
self._unit_of_measurement = SENSOR_TYPES[self.type][3]
|
||||||
|
elif self._unit_system == 'uk':
|
||||||
|
self._unit_of_measurement = SENSOR_TYPES[self.type][4]
|
||||||
|
elif self._unit_system == 'uk2':
|
||||||
|
self._unit_of_measurement = SENSOR_TYPES[self.type][5]
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -146,6 +133,11 @@ class ForeCastSensor(Entity):
|
|||||||
""" Unit of measurement of this entity, if any. """
|
""" Unit of measurement of this entity, if any. """
|
||||||
return self._unit_of_measurement
|
return self._unit_of_measurement
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_system(self):
|
||||||
|
""" Unit system of this entity. """
|
||||||
|
return self._unit_system
|
||||||
|
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Gets the latest data from Forecast.io and updates the states. """
|
""" Gets the latest data from Forecast.io and updates the states. """
|
||||||
@ -156,7 +148,14 @@ class ForeCastSensor(Entity):
|
|||||||
try:
|
try:
|
||||||
if self.type == 'summary':
|
if self.type == 'summary':
|
||||||
self._state = data.summary
|
self._state = data.summary
|
||||||
|
elif self.type == 'icon':
|
||||||
|
self._state = data.icon
|
||||||
|
elif self.type == 'nearest_storm_distance':
|
||||||
|
self._state = data.nearestStormDistance
|
||||||
|
elif self.type == 'nearest_storm_bearing':
|
||||||
|
self._state = data.nearestStormBearing
|
||||||
elif self.type == 'precip_intensity':
|
elif self.type == 'precip_intensity':
|
||||||
|
self._state = data.precipIntensity
|
||||||
if data.precipIntensity == 0:
|
if data.precipIntensity == 0:
|
||||||
self._state = 'None'
|
self._state = 'None'
|
||||||
self._unit_of_measurement = ''
|
self._unit_of_measurement = ''
|
||||||
@ -168,20 +167,14 @@ class ForeCastSensor(Entity):
|
|||||||
self._unit_of_measurement = ''
|
self._unit_of_measurement = ''
|
||||||
else:
|
else:
|
||||||
self._state = data.precipType
|
self._state = data.precipType
|
||||||
|
elif self.type == 'precip_probability':
|
||||||
|
self._state = round(data.precipProbability * 100, 1)
|
||||||
elif self.type == 'dew_point':
|
elif self.type == 'dew_point':
|
||||||
if self._unit == TEMP_CELCIUS:
|
self._state = round(data.dewPoint, 1)
|
||||||
self._state = round(data.dewPoint, 1)
|
|
||||||
elif self._unit == TEMP_FAHRENHEIT:
|
|
||||||
self._state = round(data.dewPoint * 1.8 + 32.0, 1)
|
|
||||||
else:
|
|
||||||
self._state = round(data.dewPoint, 1)
|
|
||||||
elif self.type == 'temperature':
|
elif self.type == 'temperature':
|
||||||
if self._unit == TEMP_CELCIUS:
|
self._state = round(data.temperature, 1)
|
||||||
self._state = round(data.temperature, 1)
|
elif self.type == 'apparent_temperature':
|
||||||
elif self._unit == TEMP_FAHRENHEIT:
|
self._state = round(data.apparentTemperature, 1)
|
||||||
self._state = round(data.temperature * 1.8 + 32.0, 1)
|
|
||||||
else:
|
|
||||||
self._state = round(data.temperature, 1)
|
|
||||||
elif self.type == 'wind_speed':
|
elif self.type == 'wind_speed':
|
||||||
self._state = data.windSpeed
|
self._state = data.windSpeed
|
||||||
elif self.type == 'wind_bearing':
|
elif self.type == 'wind_bearing':
|
||||||
@ -196,6 +189,7 @@ class ForeCastSensor(Entity):
|
|||||||
self._state = data.visibility
|
self._state = data.visibility
|
||||||
elif self.type == 'ozone':
|
elif self.type == 'ozone':
|
||||||
self._state = round(data.ozone, 1)
|
self._state = round(data.ozone, 1)
|
||||||
|
|
||||||
except forecastio.utils.PropertyUnavailable:
|
except forecastio.utils.PropertyUnavailable:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -203,11 +197,14 @@ class ForeCastSensor(Entity):
|
|||||||
class ForeCastData(object):
|
class ForeCastData(object):
|
||||||
""" Gets the latest data from Forecast.io. """
|
""" Gets the latest data from Forecast.io. """
|
||||||
|
|
||||||
def __init__(self, api_key, latitude, longitude):
|
def __init__(self, api_key, latitude, longitude, units):
|
||||||
self._api_key = api_key
|
self._api_key = api_key
|
||||||
self.latitude = latitude
|
self.latitude = latitude
|
||||||
self.longitude = longitude
|
self.longitude = longitude
|
||||||
self.data = None
|
self.data = None
|
||||||
|
self.unit_system = None
|
||||||
|
self.units = units
|
||||||
|
self.update()
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self):
|
def update(self):
|
||||||
@ -216,5 +213,6 @@ class ForeCastData(object):
|
|||||||
forecast = forecastio.load_forecast(self._api_key,
|
forecast = forecastio.load_forecast(self._api_key,
|
||||||
self.latitude,
|
self.latitude,
|
||||||
self.longitude,
|
self.longitude,
|
||||||
units='si')
|
units=self.units)
|
||||||
self.data = forecast.currently()
|
self.data = forecast.currently()
|
||||||
|
self.unit_system = forecast.json['flags']['units']
|
||||||
|
@ -3,51 +3,6 @@ homeassistant.components.sensor.glances
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Gathers system information of hosts which running glances.
|
Gathers system information of hosts which running glances.
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
To use the glances sensor you will need to add something like the following
|
|
||||||
to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: glances
|
|
||||||
name: Glances sensor
|
|
||||||
host: IP_ADDRESS
|
|
||||||
port: 61208
|
|
||||||
resources:
|
|
||||||
- 'disk_use_percent'
|
|
||||||
- 'disk_use'
|
|
||||||
- 'disk_free'
|
|
||||||
- 'memory_use_percent'
|
|
||||||
- 'memory_use'
|
|
||||||
- 'memory_free'
|
|
||||||
- 'swap_use_percent'
|
|
||||||
- 'swap_use'
|
|
||||||
- 'swap_free'
|
|
||||||
- 'processor_load'
|
|
||||||
- 'process_running'
|
|
||||||
- 'process_total'
|
|
||||||
- 'process_thread'
|
|
||||||
- 'process_sleeping'
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
The name of the sensor. Default is 'Glances Sensor'.
|
|
||||||
|
|
||||||
host
|
|
||||||
*Required
|
|
||||||
The IP address of your host, e.g. 192.168.1.32.
|
|
||||||
|
|
||||||
port
|
|
||||||
*Optional
|
|
||||||
The network port to connect to. Default is 61208.
|
|
||||||
|
|
||||||
resources
|
|
||||||
*Required
|
|
||||||
Resources to monitor on the host. See the configuration example above for a
|
|
||||||
list of all available conditions to monitor.
|
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/sensor.glances.html
|
https://home-assistant.io/components/sensor.glances.html
|
||||||
"""
|
"""
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
homeassistant.components.sensor.isy994
|
homeassistant.components.sensor.isy994
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for ISY994 sensors.
|
Support for ISY994 sensors.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/isy994.html
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -3,50 +3,8 @@ homeassistant.components.modbus
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for Modbus sensors.
|
Support for Modbus sensors.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.modbus.html
|
||||||
To use the Modbus sensors you will need to add something like the following to
|
|
||||||
your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: modbus
|
|
||||||
slave: 1
|
|
||||||
registers:
|
|
||||||
16:
|
|
||||||
name: My integer sensor
|
|
||||||
unit: C
|
|
||||||
24:
|
|
||||||
bits:
|
|
||||||
0:
|
|
||||||
name: My boolean sensor
|
|
||||||
2:
|
|
||||||
name: My other boolean sensor
|
|
||||||
coils:
|
|
||||||
0:
|
|
||||||
name: My coil switch
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
slave
|
|
||||||
*Required
|
|
||||||
Slave number (ignored and can be omitted if not serial Modbus).
|
|
||||||
|
|
||||||
unit
|
|
||||||
*Required
|
|
||||||
Unit to attach to value (optional, ignored for boolean sensors).
|
|
||||||
|
|
||||||
registers
|
|
||||||
*Required
|
|
||||||
Contains a list of relevant registers to read from. It can contain a
|
|
||||||
"bits" section, listing relevant bits.
|
|
||||||
|
|
||||||
coils
|
|
||||||
*Optional
|
|
||||||
Contains a list of relevant coils to read from.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
- Each named register will create an integer sensor.
|
|
||||||
- Each named bit will create a boolean sensor.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,41 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
homeassistant.components.sensor.mqtt
|
homeassistant.components.sensor.mqtt
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Allows to configure a MQTT sensor.
|
Allows to configure a MQTT sensor.
|
||||||
|
|
||||||
This generic sensor implementation uses the MQTT message payload
|
For more details about this platform, please refer to the documentation at
|
||||||
as the sensor value. If messages in this state_topic are published
|
https://home-assistant.io/components/sensor.mqtt.html
|
||||||
with RETAIN flag, the sensor will receive an instant update with
|
|
||||||
last known value. Otherwise, the initial state will be undefined.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: mqtt
|
|
||||||
name: "MQTT Sensor"
|
|
||||||
state_topic: "home/bedroom/temperature"
|
|
||||||
qos: 0
|
|
||||||
unit_of_measurement: "ºC"
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
The name of the sensor. Default is 'MQTT Sensor'.
|
|
||||||
|
|
||||||
state_topic
|
|
||||||
*Required
|
|
||||||
The MQTT topic subscribed to receive sensor values.
|
|
||||||
|
|
||||||
qos
|
|
||||||
*Optional
|
|
||||||
The maximum QoS level of the state topic. Default is 0.
|
|
||||||
|
|
||||||
unit_of_measurement
|
|
||||||
*Optional
|
|
||||||
Defines the units of measurement of the sensor, if any.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
import homeassistant.components.mqtt as mqtt
|
import homeassistant.components.mqtt as mqtt
|
||||||
@ -50,7 +20,7 @@ DEPENDENCIES = ['mqtt']
|
|||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
""" Add MQTT Sensor """
|
""" Add MQTT Sensor. """
|
||||||
|
|
||||||
if config.get('state_topic') is None:
|
if config.get('state_topic') is None:
|
||||||
_LOGGER.error("Missing required variable: state_topic")
|
_LOGGER.error("Missing required variable: state_topic")
|
||||||
@ -66,7 +36,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
|||||||
|
|
||||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||||
class MqttSensor(Entity):
|
class MqttSensor(Entity):
|
||||||
""" Represents a sensor that can be updated using MQTT """
|
""" Represents a sensor that can be updated using MQTT. """
|
||||||
def __init__(self, hass, name, state_topic, qos, unit_of_measurement):
|
def __init__(self, hass, name, state_topic, qos, unit_of_measurement):
|
||||||
self._state = "-"
|
self._state = "-"
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
|
@ -3,20 +3,8 @@ homeassistant.components.sensor.mysensors
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for MySensors sensors.
|
Support for MySensors sensors.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.mysensors.html
|
||||||
To use the MySensors sensor you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: mysensors
|
|
||||||
port: '/dev/ttyACM0'
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
port
|
|
||||||
*Required
|
|
||||||
Port of your connection to your MySensors device.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -30,14 +18,16 @@ from homeassistant.const import (
|
|||||||
CONF_PORT = "port"
|
CONF_PORT = "port"
|
||||||
CONF_DEBUG = "debug"
|
CONF_DEBUG = "debug"
|
||||||
CONF_PERSISTENCE = "persistence"
|
CONF_PERSISTENCE = "persistence"
|
||||||
|
CONF_PERSISTENCE_FILE = "persistence_file"
|
||||||
|
CONF_VERSION = "version"
|
||||||
|
|
||||||
ATTR_NODE_ID = "node_id"
|
ATTR_NODE_ID = "node_id"
|
||||||
ATTR_CHILD_ID = "child_id"
|
ATTR_CHILD_ID = "child_id"
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
REQUIREMENTS = ['https://github.com/theolind/pymysensors/archive/'
|
REQUIREMENTS = ['https://github.com/theolind/pymysensors/archive/'
|
||||||
'35b87d880147a34107da0d40cb815d75e6cb4af7.zip'
|
'd4b809c2167650691058d1e29bfd2c4b1792b4b0.zip'
|
||||||
'#pymysensors==0.2']
|
'#pymysensors==0.3']
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
@ -86,9 +76,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
persistence = config.get(CONF_PERSISTENCE, True)
|
persistence = config.get(CONF_PERSISTENCE, True)
|
||||||
|
persistence_file = config.get(CONF_PERSISTENCE_FILE, 'mysensors.pickle')
|
||||||
|
version = config.get(CONF_VERSION, '1.4')
|
||||||
|
|
||||||
gateway = mysensors.SerialGateway(port, sensor_update,
|
gateway = mysensors.SerialGateway(port, sensor_update,
|
||||||
persistence=persistence)
|
persistence=persistence,
|
||||||
|
persistence_file=persistence_file,
|
||||||
|
protocol_version=version)
|
||||||
gateway.metric = is_metric
|
gateway.metric = is_metric
|
||||||
gateway.debug = config.get(CONF_DEBUG, False)
|
gateway.debug = config.get(CONF_DEBUG, False)
|
||||||
gateway.start()
|
gateway.start()
|
||||||
|
@ -3,43 +3,8 @@ homeassistant.components.sensor.openweathermap
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
OpenWeatherMap (OWM) service.
|
OpenWeatherMap (OWM) service.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.openweathermap.html
|
||||||
To use the OpenWeatherMap sensor you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: openweathermap
|
|
||||||
api_key: YOUR_APP_KEY
|
|
||||||
forecast: 0 or 1
|
|
||||||
monitored_conditions:
|
|
||||||
- weather
|
|
||||||
- temperature
|
|
||||||
- wind_speed
|
|
||||||
- humidity
|
|
||||||
- pressure
|
|
||||||
- clouds
|
|
||||||
- rain
|
|
||||||
- snow
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
api_key
|
|
||||||
*Required
|
|
||||||
To retrieve this value log into your account at http://openweathermap.org/
|
|
||||||
|
|
||||||
forecast
|
|
||||||
*Optional
|
|
||||||
Enables the forecast. The default is to display the current conditions.
|
|
||||||
|
|
||||||
monitored_conditions
|
|
||||||
*Required
|
|
||||||
Conditions to monitor. See the configuration example above for a
|
|
||||||
list of all available conditions to monitor.
|
|
||||||
|
|
||||||
Details for the API : http://bugs.openweathermap.org/projects/api/wiki
|
|
||||||
|
|
||||||
Only metric measurements are supported at the moment.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -1,41 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
"""
|
||||||
homeassistant.components.sensor.rpi_gpio
|
homeassistant.components.sensor.rpi_gpio
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Allows to configure a binary state sensor using RPi GPIO.
|
Allows to configure a binary state sensor using RPi GPIO.
|
||||||
To avoid having to run Home Assistant as root when using this component,
|
|
||||||
run a Raspbian version released at or after September 29, 2015.
|
|
||||||
|
|
||||||
sensor:
|
For more details about this platform, please refer to the documentation at
|
||||||
platform: rpi_gpio
|
https://home-assistant.io/components/sensor.rpi_gpio.html
|
||||||
pull_mode: "UP"
|
|
||||||
value_high: "Active"
|
|
||||||
value_low: "Inactive"
|
|
||||||
ports:
|
|
||||||
11: PIR Office
|
|
||||||
12: PIR Bedroom
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
pull_mode
|
|
||||||
*Optional
|
|
||||||
The internal pull to use (UP or DOWN). Default is UP.
|
|
||||||
|
|
||||||
value_high
|
|
||||||
*Optional
|
|
||||||
The value of the sensor when the port is HIGH. Default is "HIGH".
|
|
||||||
|
|
||||||
value_low
|
|
||||||
*Optional
|
|
||||||
The value of the sensor when the port is LOW. Default is "LOW".
|
|
||||||
|
|
||||||
bouncetime
|
|
||||||
*Optional
|
|
||||||
The time in milliseconds for port debouncing. Default is 50ms.
|
|
||||||
|
|
||||||
ports
|
|
||||||
*Required
|
|
||||||
An array specifying the GPIO ports to use and the name to use in the frontend.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
@ -1,47 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.components.sensor.sabnzbd
|
homeassistant.components.sensor.sabnzbd
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Monitors SABnzbd NZB client API
|
Monitors SABnzbd NZB client API.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.sabnzbd.html
|
||||||
To use the SABnzbd sensor you will need to add something like the following to
|
|
||||||
your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: sabnzbd
|
|
||||||
name: SAB
|
|
||||||
api_key: YOUR_API_KEY
|
|
||||||
base_url: YOUR_SABNZBD_BASE_URL
|
|
||||||
monitored_variables:
|
|
||||||
- type: 'current_status'
|
|
||||||
- type: 'speed'
|
|
||||||
- type: 'queue_size'
|
|
||||||
- type: 'queue_remaining'
|
|
||||||
- type: 'disk_size'
|
|
||||||
- type: 'disk_free'
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
base_url
|
|
||||||
*Required
|
|
||||||
This is the base URL of your SABnzbd instance including the port number if not
|
|
||||||
running on 80, e.g. http://192.168.1.32:8124/
|
|
||||||
|
|
||||||
name
|
|
||||||
*Optional
|
|
||||||
The name to use when displaying this SABnzbd instance.
|
|
||||||
|
|
||||||
monitored_variables
|
|
||||||
*Required
|
|
||||||
An array specifying the variables to monitor.
|
|
||||||
|
|
||||||
These are the variables for the monitored_variables array:
|
|
||||||
|
|
||||||
type
|
|
||||||
*Required
|
|
||||||
The variable you wish to monitor, see the configuration example above for a
|
|
||||||
list of all available variables.
|
|
||||||
"""
|
"""
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -4,30 +4,8 @@ homeassistant.components.sensor.swiss_public_transport
|
|||||||
The Swiss public transport sensor will give you the next two departure times
|
The Swiss public transport sensor will give you the next two departure times
|
||||||
from a given location to another one. This sensor is limited to Switzerland.
|
from a given location to another one. This sensor is limited to Switzerland.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.swiss_public_transport.html
|
||||||
To use the Swiss public transport sensor you will need to add something like
|
|
||||||
the following to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: swiss_public_transport
|
|
||||||
from: STATION_ID
|
|
||||||
to: STATION_ID
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
from
|
|
||||||
*Required
|
|
||||||
Start station/stop of your trip. To search for the ID of the station, use the
|
|
||||||
an URL like this: http://transport.opendata.ch/v1/locations?query=Wankdorf
|
|
||||||
to query for the station. If the score is 100 ("score":"100" in the response),
|
|
||||||
it is a perfect match.
|
|
||||||
|
|
||||||
to
|
|
||||||
*Required
|
|
||||||
Destination station/stop of the trip. Same procedure as for the start station.
|
|
||||||
|
|
||||||
Details for the API : http://transport.opendata.ch
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
@ -40,6 +18,12 @@ from homeassistant.helpers.entity import Entity
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_RESOURCE = 'http://transport.opendata.ch/v1/'
|
_RESOURCE = 'http://transport.opendata.ch/v1/'
|
||||||
|
|
||||||
|
ATTR_DEPARTURE_TIME1 = 'Next departure'
|
||||||
|
ATTR_DEPARTURE_TIME2 = 'Next on departure'
|
||||||
|
ATTR_START = 'Start'
|
||||||
|
ATTR_TARGET = 'Destination'
|
||||||
|
ATTR_REMAINING_TIME = 'Remaining time'
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
||||||
|
|
||||||
@ -60,8 +44,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
_LOGGER.exception(
|
_LOGGER.exception(
|
||||||
"Unable to determine stations. "
|
"Unable to determine stations. "
|
||||||
"Check your settings and/or the availability of opendata.ch")
|
"Check your settings and/or the availability of opendata.ch")
|
||||||
|
return False
|
||||||
return None
|
|
||||||
|
|
||||||
dev = []
|
dev = []
|
||||||
data = PublicTransportData(journey)
|
data = PublicTransportData(journey)
|
||||||
@ -75,7 +58,9 @@ class SwissPublicTransportSensor(Entity):
|
|||||||
|
|
||||||
def __init__(self, data, journey):
|
def __init__(self, data, journey):
|
||||||
self.data = data
|
self.data = data
|
||||||
self._name = '{}-{}'.format(journey[2], journey[3])
|
self._name = 'Next Departure'
|
||||||
|
self._from = journey[2]
|
||||||
|
self._to = journey[3]
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -88,12 +73,26 @@ class SwissPublicTransportSensor(Entity):
|
|||||||
""" Returns the state of the device. """
|
""" Returns the state of the device. """
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
""" Returns the state attributes. """
|
||||||
|
if self._times is not None:
|
||||||
|
return {
|
||||||
|
ATTR_DEPARTURE_TIME1: self._times[0],
|
||||||
|
ATTR_DEPARTURE_TIME2: self._times[1],
|
||||||
|
ATTR_START: self._from,
|
||||||
|
ATTR_TARGET: self._to,
|
||||||
|
ATTR_REMAINING_TIME: '{}'.format(
|
||||||
|
':'.join(str(self._times[2]).split(':')[:2]))
|
||||||
|
}
|
||||||
|
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
def update(self):
|
def update(self):
|
||||||
""" Gets the latest data from opendata.ch and updates the states. """
|
""" Gets the latest data from opendata.ch and updates the states. """
|
||||||
times = self.data.update()
|
self.data.update()
|
||||||
|
self._times = self.data.times
|
||||||
try:
|
try:
|
||||||
self._state = ', '.join(times)
|
self._state = self._times[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -105,6 +104,7 @@ class PublicTransportData(object):
|
|||||||
def __init__(self, journey):
|
def __init__(self, journey):
|
||||||
self.start = journey[0]
|
self.start = journey[0]
|
||||||
self.destination = journey[1]
|
self.destination = journey[1]
|
||||||
|
self.times = {}
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self):
|
def update(self):
|
||||||
@ -117,16 +117,21 @@ class PublicTransportData(object):
|
|||||||
'to=' + self.destination + '&' +
|
'to=' + self.destination + '&' +
|
||||||
'fields[]=connections/from/departureTimestamp/&' +
|
'fields[]=connections/from/departureTimestamp/&' +
|
||||||
'fields[]=connections/',
|
'fields[]=connections/',
|
||||||
timeout=10)
|
timeout=30)
|
||||||
connections = response.json()['connections'][:2]
|
connections = response.json()['connections'][:2]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return [
|
self.times = [
|
||||||
dt_util.datetime_to_time_str(
|
dt_util.datetime_to_time_str(
|
||||||
dt_util.as_local(dt_util.utc_from_timestamp(
|
dt_util.as_local(dt_util.utc_from_timestamp(
|
||||||
item['from']['departureTimestamp']))
|
item['from']['departureTimestamp']))
|
||||||
)
|
)
|
||||||
for item in connections
|
for item in connections
|
||||||
]
|
]
|
||||||
|
self.times.append(
|
||||||
|
dt_util.as_local(
|
||||||
|
dt_util.utc_from_timestamp(
|
||||||
|
connections[0]['from']['departureTimestamp'])) -
|
||||||
|
dt_util.as_local(dt_util.utcnow()))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return ['n/a']
|
self.times = ['n/a']
|
||||||
|
@ -3,60 +3,8 @@ homeassistant.components.sensor.systemmonitor
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Shows system monitor values such as: disk, memory, and processor use.
|
Shows system monitor values such as: disk, memory, and processor use.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.systemmonitor.html
|
||||||
To use the System monitor sensor you will need to add something like the
|
|
||||||
following to your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: systemmonitor
|
|
||||||
resources:
|
|
||||||
- type: 'disk_use_percent'
|
|
||||||
arg: '/'
|
|
||||||
- type: 'disk_use'
|
|
||||||
arg: '/home'
|
|
||||||
- type: 'disk_free'
|
|
||||||
arg: '/'
|
|
||||||
- type: 'memory_use_percent'
|
|
||||||
- type: 'memory_use'
|
|
||||||
- type: 'memory_free'
|
|
||||||
- type: 'swap_use_percent'
|
|
||||||
- type: 'swap_use'
|
|
||||||
- type: 'swap_free'
|
|
||||||
- type: 'network_in'
|
|
||||||
arg: 'eth0'
|
|
||||||
- type: 'network_out'
|
|
||||||
arg: 'eth0'
|
|
||||||
- type: 'packets_in'
|
|
||||||
arg: 'eth0'
|
|
||||||
- type: 'packets_out'
|
|
||||||
arg: 'eth0'
|
|
||||||
- type: 'ipv4_address'
|
|
||||||
arg: 'eth0'
|
|
||||||
- type: 'ipv6_address'
|
|
||||||
arg: 'eth0'
|
|
||||||
- type: 'processor_use'
|
|
||||||
- type: 'process'
|
|
||||||
arg: 'octave-cli'
|
|
||||||
- type: 'last_boot'
|
|
||||||
- type: 'since_last_boot'
|
|
||||||
|
|
||||||
Variables:
|
|
||||||
|
|
||||||
resources
|
|
||||||
*Required
|
|
||||||
An array specifying the variables to monitor.
|
|
||||||
|
|
||||||
These are the variables for the resources array:
|
|
||||||
|
|
||||||
type
|
|
||||||
*Required
|
|
||||||
The variable you wish to monitor, see the configuration example above for a
|
|
||||||
sample list of variables.
|
|
||||||
|
|
||||||
arg
|
|
||||||
*Optional
|
|
||||||
Additional details for the type, eg. path, binary name, etc.
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -64,7 +12,7 @@ import homeassistant.util.dt as dt_util
|
|||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.const import STATE_ON, STATE_OFF
|
from homeassistant.const import STATE_ON, STATE_OFF
|
||||||
|
|
||||||
REQUIREMENTS = ['psutil==3.0.0']
|
REQUIREMENTS = ['psutil==3.2.2']
|
||||||
SENSOR_TYPES = {
|
SENSOR_TYPES = {
|
||||||
'disk_use_percent': ['Disk Use', '%'],
|
'disk_use_percent': ['Disk Use', '%'],
|
||||||
'disk_use': ['Disk Use', 'GiB'],
|
'disk_use': ['Disk Use', 'GiB'],
|
||||||
|
@ -3,24 +3,8 @@ homeassistant.components.sensor.tellstick
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Shows sensor values from Tellstick sensors.
|
Shows sensor values from Tellstick sensors.
|
||||||
|
|
||||||
Possible config keys:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.tellstick.html
|
||||||
id of the sensor: Name the sensor with ID
|
|
||||||
135=Outside
|
|
||||||
|
|
||||||
only_named: Only show the named sensors
|
|
||||||
only_named=1
|
|
||||||
|
|
||||||
temperature_scale: The scale of the temperature value
|
|
||||||
temperature_scale=°C
|
|
||||||
|
|
||||||
datatype_mask: mask to determine which sensor values to show based on
|
|
||||||
https://tellcore-py.readthedocs.org
|
|
||||||
/en/v1.0.4/constants.html#module-tellcore.constants
|
|
||||||
|
|
||||||
datatype_mask=1 # only show temperature
|
|
||||||
datatype_mask=12 # only show rain rate and rain total
|
|
||||||
datatype_mask=127 # show all sensor values
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
@ -3,13 +3,8 @@ homeassistant.components.sensor.temper
|
|||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Support for getting temperature from TEMPer devices.
|
Support for getting temperature from TEMPer devices.
|
||||||
|
|
||||||
Configuration:
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/sensor.temper.html
|
||||||
To use the temper sensors you will need to add something like the following to
|
|
||||||
your configuration.yaml file.
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
platform: temper
|
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user