mirror of
https://github.com/elisspace/core.git
synced 2026-10-02 15:30:00 +00:00
gitignore ready for emacs
This commit is contained in:
@@ -6,7 +6,7 @@ Component to interface with various sensors that can be monitored.
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.components import wink, zwave
|
||||
from homeassistant.components import wink, zwave, isy994, verisure
|
||||
|
||||
DOMAIN = 'sensor'
|
||||
DEPENDENCIES = []
|
||||
@@ -18,6 +18,8 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
DISCOVERY_PLATFORMS = {
|
||||
wink.DISCOVER_SENSORS: 'wink',
|
||||
zwave.DISCOVER_SENSORS: 'zwave',
|
||||
isy994.DISCOVER_SENSORS: 'isy994',
|
||||
verisure.DISCOVER_SENSORS: 'verisure'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
homeassistant.components.sensor.arduino
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for getting information from Arduino pins. Only analog pins are
|
||||
supported.
|
||||
|
||||
Configuration:
|
||||
|
||||
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 homeassistant.components.arduino as arduino
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import DEVICE_DEFAULT_NAME
|
||||
|
||||
DEPENDENCIES = ['arduino']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the Arduino platform. """
|
||||
|
||||
# Verify that the Arduino board is present
|
||||
if arduino.BOARD is None:
|
||||
_LOGGER.error('A connection has not been made to the Arduino board.')
|
||||
return False
|
||||
|
||||
sensors = []
|
||||
pins = config.get('pins')
|
||||
for pinnum, pin in pins.items():
|
||||
if pin.get('name'):
|
||||
sensors.append(ArduinoSensor(pin.get('name'),
|
||||
pinnum,
|
||||
'analog'))
|
||||
add_devices(sensors)
|
||||
|
||||
|
||||
class ArduinoSensor(Entity):
|
||||
""" Represents an Arduino Sensor. """
|
||||
def __init__(self, name, pin, pin_type):
|
||||
self._pin = pin
|
||||
self._name = name or DEVICE_DEFAULT_NAME
|
||||
self.pin_type = pin_type
|
||||
self.direction = 'in'
|
||||
self._value = None
|
||||
|
||||
arduino.BOARD.set_mode(self._pin, self.direction, self.pin_type)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the sensor. """
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Get the name of the sensor. """
|
||||
return self._name
|
||||
|
||||
def update(self):
|
||||
""" Get the latest value from the pin. """
|
||||
self._value = arduino.BOARD.get_analog_inputs()[self._pin][1]
|
||||
@@ -0,0 +1,150 @@
|
||||
"""
|
||||
homeassistant.components.sensor.arest
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The arest sensor will consume an exposed aREST API of a device.
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the arest sensor you will need to add something like the following
|
||||
to your configuration.yaml file.
|
||||
|
||||
sensor:
|
||||
platform: arest
|
||||
resource: http://IP_ADDRESS
|
||||
monitored_variables:
|
||||
- name: temperature
|
||||
unit: '°C'
|
||||
- name: humidity
|
||||
unit: '%'
|
||||
|
||||
Variables:
|
||||
|
||||
resource:
|
||||
*Required
|
||||
IP address of the device that is exposing an aREST API.
|
||||
|
||||
These are the variables for the monitored_variables array:
|
||||
|
||||
name
|
||||
*Required
|
||||
The name of the variable you wish to monitor.
|
||||
|
||||
unit
|
||||
*Optional
|
||||
Defines the units of measurement of the sensor, if any.
|
||||
|
||||
Details for the API: http://arest.io
|
||||
|
||||
Format of a default JSON response by aREST:
|
||||
{
|
||||
"variables":{
|
||||
"temperature":21,
|
||||
"humidity":89
|
||||
},
|
||||
"id":"device008",
|
||||
"name":"Bedroom",
|
||||
"connected":true
|
||||
}
|
||||
"""
|
||||
import logging
|
||||
from requests import get, exceptions
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Return cached results if last scan was less then this time ago
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Get the aREST sensor. """
|
||||
|
||||
resource = config.get('resource', None)
|
||||
|
||||
try:
|
||||
response = get(resource)
|
||||
except exceptions.MissingSchema:
|
||||
_LOGGER.error("Missing resource or schema in configuration. "
|
||||
"Add http:// to your URL.")
|
||||
return False
|
||||
except exceptions.ConnectionError:
|
||||
_LOGGER.error("No route to device. "
|
||||
"Please check the IP address in the configuration file.")
|
||||
return False
|
||||
|
||||
rest = ArestData(resource)
|
||||
|
||||
dev = []
|
||||
for variable in config['monitored_variables']:
|
||||
if 'unit' not in variable:
|
||||
variable['unit'] = ' '
|
||||
if variable['name'] not in response.json()['variables']:
|
||||
_LOGGER.error('Variable: "%s" does not exist', variable['name'])
|
||||
else:
|
||||
dev.append(ArestSensor(rest,
|
||||
response.json()['name'],
|
||||
variable['name'],
|
||||
variable['unit']))
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
class ArestSensor(Entity):
|
||||
""" Implements an aREST sensor. """
|
||||
|
||||
def __init__(self, rest, location, variable, unit_of_measurement):
|
||||
self.rest = rest
|
||||
self._name = '{} {}'.format(location.title(), variable.title())
|
||||
self._variable = variable
|
||||
self._state = 'n/a'
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" The name of the sensor. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit the value is expressed in. """
|
||||
return self._unit_of_measurement
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
def update(self):
|
||||
""" Gets the latest data from aREST API and updates the state. """
|
||||
self.rest.update()
|
||||
values = self.rest.data
|
||||
|
||||
if 'error' in values:
|
||||
self._state = values['error']
|
||||
else:
|
||||
self._state = values[self._variable]
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class ArestData(object):
|
||||
""" Class for handling the data retrieval. """
|
||||
|
||||
def __init__(self, resource):
|
||||
self.resource = resource
|
||||
self.data = dict()
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
""" Gets the latest data from aREST device. """
|
||||
try:
|
||||
response = get(self.resource)
|
||||
if 'error' in self.data:
|
||||
del self.data['error']
|
||||
self.data = response.json()['variables']
|
||||
except exceptions.ConnectionError:
|
||||
_LOGGER.error("No route to device. Is device offline?")
|
||||
self.data['error'] = 'n/a'
|
||||
@@ -0,0 +1,250 @@
|
||||
"""
|
||||
homeassistant.components.sensor.bitcoin
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Bitcoin information service that uses blockchain.info and its online wallet.
|
||||
|
||||
Configuration:
|
||||
|
||||
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
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
|
||||
REQUIREMENTS = ['blockchain==1.1.2']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
OPTION_TYPES = {
|
||||
'wallet': ['Wallet balance', 'BTC'],
|
||||
'exchangerate': ['Exchange rate (1 BTC)', ''],
|
||||
'trade_volume_btc': ['Trade volume', 'BTC'],
|
||||
'miners_revenue_usd': ['Miners revenue', 'USD'],
|
||||
'btc_mined': ['Mined', 'BTC'],
|
||||
'trade_volume_usd': ['Trade volume', 'USD'],
|
||||
'difficulty': ['Difficulty', ''],
|
||||
'minutes_between_blocks': ['Time between Blocks', 'min'],
|
||||
'number_of_transactions': ['No. of Transactions', ''],
|
||||
'hash_rate': ['Hash rate', 'PH/s'],
|
||||
'timestamp': ['Timestamp', ''],
|
||||
'mined_blocks': ['Minded Blocks', ''],
|
||||
'blocks_size': ['Block size', ''],
|
||||
'total_fees_btc': ['Total fees', 'BTC'],
|
||||
'total_btc_sent': ['Total sent', 'BTC'],
|
||||
'estimated_btc_sent': ['Estimated sent', 'BTC'],
|
||||
'total_btc': ['Total', 'BTC'],
|
||||
'total_blocks': ['Total Blocks', ''],
|
||||
'next_retarget': ['Next retarget', ''],
|
||||
'estimated_transaction_volume_usd': ['Est. Transaction volume', 'USD'],
|
||||
'miners_revenue_btc': ['Miners revenue', 'BTC'],
|
||||
'market_price_usd': ['Market price', 'USD']
|
||||
}
|
||||
|
||||
# Return cached results if last scan was less then this time ago
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Get the Bitcoin sensor. """
|
||||
|
||||
try:
|
||||
from blockchain.wallet import Wallet
|
||||
from blockchain import exchangerates, exceptions
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import blockchain. "
|
||||
"Did you maybe not install the 'blockchain' package?")
|
||||
|
||||
return False
|
||||
|
||||
wallet_id = config.get('wallet', None)
|
||||
password = config.get('password', None)
|
||||
currency = config.get('currency', 'USD')
|
||||
|
||||
if currency not in exchangerates.get_ticker():
|
||||
_LOGGER.error('Currency "%s" is not available. Using "USD".', currency)
|
||||
currency = 'USD'
|
||||
|
||||
wallet = Wallet(wallet_id, password)
|
||||
|
||||
try:
|
||||
wallet.get_balance()
|
||||
except exceptions.APIException as error:
|
||||
_LOGGER.error(error)
|
||||
wallet = None
|
||||
|
||||
data = BitcoinData()
|
||||
dev = []
|
||||
if wallet is not None and password is not None:
|
||||
dev.append(BitcoinSensor(data, 'wallet', currency, wallet))
|
||||
|
||||
for variable in config['display_options']:
|
||||
if variable not in OPTION_TYPES:
|
||||
_LOGGER.error('Option type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(BitcoinSensor(data, variable, currency))
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class BitcoinSensor(Entity):
|
||||
""" Implements a Bitcoin sensor. """
|
||||
|
||||
def __init__(self, data, option_type, currency, wallet=''):
|
||||
self.data = data
|
||||
self._name = OPTION_TYPES[option_type][0]
|
||||
self._unit_of_measurement = OPTION_TYPES[option_type][1]
|
||||
self._currency = currency
|
||||
self._wallet = wallet
|
||||
self.type = option_type
|
||||
self._state = None
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device. """
|
||||
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
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def update(self):
|
||||
""" Gets the latest data and updates the states. """
|
||||
|
||||
self.data.update()
|
||||
stats = self.data.stats
|
||||
ticker = self.data.ticker
|
||||
|
||||
# pylint: disable=no-member
|
||||
if self.type == 'wallet' and self._wallet is not None:
|
||||
self._state = '{0:.8f}'.format(self._wallet.get_balance() *
|
||||
0.00000001)
|
||||
elif self.type == 'exchangerate':
|
||||
self._state = ticker[self._currency].p15min
|
||||
self._unit_of_measurement = self._currency
|
||||
elif self.type == 'trade_volume_btc':
|
||||
self._state = '{0:.1f}'.format(stats.trade_volume_btc)
|
||||
elif self.type == 'miners_revenue_usd':
|
||||
self._state = '{0:.0f}'.format(stats.miners_revenue_usd)
|
||||
elif self.type == 'btc_mined':
|
||||
self._state = '{}'.format(stats.btc_mined * 0.00000001)
|
||||
elif self.type == 'trade_volume_usd':
|
||||
self._state = '{0:.1f}'.format(stats.trade_volume_usd)
|
||||
elif self.type == 'difficulty':
|
||||
self._state = '{0:.0f}'.format(stats.difficulty)
|
||||
elif self.type == 'minutes_between_blocks':
|
||||
self._state = '{0:.2f}'.format(stats.minutes_between_blocks)
|
||||
elif self.type == 'number_of_transactions':
|
||||
self._state = '{}'.format(stats.number_of_transactions)
|
||||
elif self.type == 'hash_rate':
|
||||
self._state = '{0:.1f}'.format(stats.hash_rate * 0.000001)
|
||||
elif self.type == 'timestamp':
|
||||
self._state = stats.timestamp
|
||||
elif self.type == 'mined_blocks':
|
||||
self._state = '{}'.format(stats.mined_blocks)
|
||||
elif self.type == 'blocks_size':
|
||||
self._state = '{0:.1f}'.format(stats.blocks_size)
|
||||
elif self.type == 'total_fees_btc':
|
||||
self._state = '{0:.2f}'.format(stats.total_fees_btc * 0.00000001)
|
||||
elif self.type == 'total_btc_sent':
|
||||
self._state = '{0:.2f}'.format(stats.total_btc_sent * 0.00000001)
|
||||
elif self.type == 'estimated_btc_sent':
|
||||
self._state = '{0:.2f}'.format(stats.estimated_btc_sent *
|
||||
0.00000001)
|
||||
elif self.type == 'total_btc':
|
||||
self._state = '{0:.2f}'.format(stats.total_btc * 0.00000001)
|
||||
elif self.type == 'total_blocks':
|
||||
self._state = '{0:.2f}'.format(stats.total_blocks)
|
||||
elif self.type == 'next_retarget':
|
||||
self._state = '{0:.2f}'.format(stats.next_retarget)
|
||||
elif self.type == 'estimated_transaction_volume_usd':
|
||||
self._state = '{0:.2f}'.format(
|
||||
stats.estimated_transaction_volume_usd)
|
||||
elif self.type == 'miners_revenue_btc':
|
||||
self._state = '{0:.1f}'.format(stats.miners_revenue_btc *
|
||||
0.00000001)
|
||||
elif self.type == 'market_price_usd':
|
||||
self._state = '{0:.2f}'.format(stats.market_price_usd)
|
||||
|
||||
|
||||
class BitcoinData(object):
|
||||
""" Gets the latest data and updates the states. """
|
||||
|
||||
def __init__(self):
|
||||
self.stats = None
|
||||
self.ticker = None
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
""" Gets the latest data from blockchain.info. """
|
||||
|
||||
from blockchain import statistics, exchangerates
|
||||
|
||||
self.stats = statistics.get()
|
||||
self.ticker = exchangerates.get_ticker()
|
||||
@@ -1,4 +1,8 @@
|
||||
""" Support for Wink sensors. """
|
||||
"""
|
||||
homeassistant.components.sensor.demo
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Demo platform that has a couple of fake sensors.
|
||||
"""
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import TEMP_CELCIUS, ATTR_BATTERY_LEVEL
|
||||
|
||||
@@ -9,6 +13,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
add_devices([
|
||||
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS, 12),
|
||||
DemoSensor('Outside Humidity', 54, '%', None),
|
||||
DemoSensor('Alarm back', 'Armed', None, None),
|
||||
])
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
homeassistant.components.sensor.dht
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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:
|
||||
|
||||
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
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.const import TEMP_FAHRENHEIT
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
# update this requirement to upstream as soon as it supports python3
|
||||
REQUIREMENTS = ['http://github.com/mala-zaba/Adafruit_Python_DHT/archive/'
|
||||
'4101340de8d2457dd194bca1e8d11cbfc237e919.zip'
|
||||
'#Adafruit_DHT==1.1.0']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
SENSOR_TYPES = {
|
||||
'temperature': ['Temperature', ''],
|
||||
'humidity': ['Humidity', '%']
|
||||
}
|
||||
# Return cached results if last scan was less then this time ago
|
||||
# DHT11 is able to deliver data once per second, DHT22 once every two
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Get the DHT sensor. """
|
||||
|
||||
try:
|
||||
import Adafruit_DHT
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import Adafruit_DHT. "
|
||||
"Did you maybe not install the 'Adafruit_DHT' package?")
|
||||
|
||||
return False
|
||||
|
||||
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
|
||||
unit = hass.config.temperature_unit
|
||||
available_sensors = {
|
||||
"DHT11": Adafruit_DHT.DHT11,
|
||||
"DHT22": Adafruit_DHT.DHT22,
|
||||
"AM2302": Adafruit_DHT.AM2302
|
||||
}
|
||||
sensor = available_sensors[config['sensor']]
|
||||
|
||||
pin = config['pin']
|
||||
|
||||
if not sensor or not pin:
|
||||
_LOGGER.error(
|
||||
"Config error "
|
||||
"Please check your settings for DHT, sensor not supported.")
|
||||
return None
|
||||
|
||||
data = DHTClient(Adafruit_DHT, sensor, pin)
|
||||
dev = []
|
||||
try:
|
||||
for variable in config['monitored_conditions']:
|
||||
if variable not in SENSOR_TYPES:
|
||||
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(DHTSensor(data, variable, unit))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class DHTSensor(Entity):
|
||||
""" Implements an DHT sensor. """
|
||||
|
||||
def __init__(self, dht_client, sensor_type, temp_unit):
|
||||
self.client_name = 'DHT sensor'
|
||||
self._name = SENSOR_TYPES[sensor_type][0]
|
||||
self.dht_client = dht_client
|
||||
self.temp_unit = temp_unit
|
||||
self.type = sensor_type
|
||||
self._state = None
|
||||
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return '{} {}'.format(self.client_name, self._name)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
return self._unit_of_measurement
|
||||
|
||||
def update(self):
|
||||
""" Gets the latest data from the DHT and updates the states. """
|
||||
|
||||
self.dht_client.update()
|
||||
data = self.dht_client.data
|
||||
|
||||
if self.type == 'temperature':
|
||||
self._state = round(data['temperature'], 1)
|
||||
if self.temp_unit == TEMP_FAHRENHEIT:
|
||||
self._state = round(data['temperature'] * 1.8 + 32, 1)
|
||||
elif self.type == 'humidity':
|
||||
self._state = round(data['humidity'], 1)
|
||||
|
||||
|
||||
class DHTClient(object):
|
||||
""" Gets the latest data from the DHT sensor. """
|
||||
|
||||
def __init__(self, adafruit_dht, sensor, pin):
|
||||
self.adafruit_dht = adafruit_dht
|
||||
self.sensor = sensor
|
||||
self.pin = pin
|
||||
self.data = dict()
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
""" Gets the latest data the DHT sensor. """
|
||||
humidity, temperature = self.adafruit_dht.read_retry(self.sensor,
|
||||
self.pin)
|
||||
if temperature:
|
||||
self.data['temperature'] = temperature
|
||||
if humidity:
|
||||
self.data['humidity'] = humidity
|
||||
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
homeassistant.components.sensor.efergy
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Monitors home energy use as measured by an efergy engage hub using its
|
||||
(unofficial, undocumented) API.
|
||||
|
||||
Configuration:
|
||||
|
||||
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
|
||||
from requests import get
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_RESOURCE = 'https://engage.efergy.com/mobile_proxy/'
|
||||
SENSOR_TYPES = {
|
||||
'instant_readings': ['Energy Usage', 'kW'],
|
||||
'budget': ['Energy Budget', ''],
|
||||
'cost': ['Energy Cost', ''],
|
||||
}
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the Efergy sensor. """
|
||||
app_token = config.get("app_token")
|
||||
if not app_token:
|
||||
_LOGGER.error(
|
||||
"Configuration Error"
|
||||
"Please make sure you have configured your app token")
|
||||
return None
|
||||
utc_offset = str(config.get("utc_offset"))
|
||||
dev = []
|
||||
for variable in config['monitored_variables']:
|
||||
if 'period' not in variable:
|
||||
variable['period'] = ''
|
||||
if 'currency' not in variable:
|
||||
variable['currency'] = ''
|
||||
if variable['type'] not in SENSOR_TYPES:
|
||||
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(EfergySensor(variable['type'], app_token, utc_offset,
|
||||
variable['period'], variable['currency']))
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class EfergySensor(Entity):
|
||||
""" Implements an Efergy sensor. """
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, sensor_type, app_token, utc_offset, period, currency):
|
||||
self._name = SENSOR_TYPES[sensor_type][0]
|
||||
self.type = sensor_type
|
||||
self.app_token = app_token
|
||||
self.utc_offset = utc_offset
|
||||
self._state = None
|
||||
self.period = period
|
||||
self.currency = currency
|
||||
if self.type == 'cost':
|
||||
self._unit_of_measurement = self.currency + '/' + self.period
|
||||
else:
|
||||
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
return self._unit_of_measurement
|
||||
|
||||
def update(self):
|
||||
""" Gets the Efergy monitor data from the web service. """
|
||||
if self.type == 'instant_readings':
|
||||
url_string = _RESOURCE + 'getInstant?token=' + self.app_token
|
||||
response = get(url_string)
|
||||
self._state = response.json()['reading'] / 1000
|
||||
elif self.type == 'budget':
|
||||
url_string = _RESOURCE + 'getBudget?token=' + self.app_token
|
||||
response = get(url_string)
|
||||
self._state = response.json()['status']
|
||||
elif self.type == 'cost':
|
||||
url_string = _RESOURCE + 'getCost?token=' + self.app_token \
|
||||
+ '&offset=' + self.utc_offset + '&period=' \
|
||||
+ self.period
|
||||
response = get(url_string)
|
||||
self._state = response.json()['sum']
|
||||
else:
|
||||
self._state = 'Unknown'
|
||||
@@ -0,0 +1,220 @@
|
||||
"""
|
||||
homeassistant.components.sensor.forecast
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Forecast.io weather service.
|
||||
|
||||
Configuration:
|
||||
|
||||
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
|
||||
from datetime import timedelta
|
||||
|
||||
REQUIREMENTS = ['python-forecastio==1.3.3']
|
||||
|
||||
try:
|
||||
import forecastio
|
||||
except ImportError:
|
||||
forecastio = None
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS, TEMP_FAHRENHEIT)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
SENSOR_TYPES = {
|
||||
'summary': ['Summary', ''],
|
||||
'precip_type': ['Precip', ''],
|
||||
'precip_intensity': ['Precip intensity', 'mm'],
|
||||
'temperature': ['Temperature', ''],
|
||||
'dew_point': ['Dew point', '°C'],
|
||||
'wind_speed': ['Wind Speed', 'm/s'],
|
||||
'wind_bearing': ['Wind Bearing', '°'],
|
||||
'cloud_cover': ['Cloud coverage', '%'],
|
||||
'humidity': ['Humidity', '%'],
|
||||
'pressure': ['Pressure', 'mBar'],
|
||||
'visibility': ['Visibility', 'km'],
|
||||
'ozone': ['Ozone', 'DU'],
|
||||
}
|
||||
|
||||
# Return cached results if last scan was less then this time ago
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Get the Forecast.io sensor. """
|
||||
|
||||
global forecastio # pylint: disable=invalid-name
|
||||
if forecastio is None:
|
||||
import forecastio as forecastio_
|
||||
forecastio = forecastio_
|
||||
|
||||
if None in (hass.config.latitude, hass.config.longitude):
|
||||
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
||||
return False
|
||||
|
||||
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
|
||||
unit = hass.config.temperature_unit
|
||||
|
||||
try:
|
||||
forecast = forecastio.load_forecast(config.get(CONF_API_KEY, None),
|
||||
hass.config.latitude,
|
||||
hass.config.longitude)
|
||||
forecast.currently()
|
||||
except ValueError:
|
||||
_LOGGER.error(
|
||||
"Connection error "
|
||||
"Please check your settings for Forecast.io.")
|
||||
return False
|
||||
|
||||
data = ForeCastData(config.get(CONF_API_KEY, None),
|
||||
hass.config.latitude,
|
||||
hass.config.longitude)
|
||||
|
||||
dev = []
|
||||
for variable in config['monitored_conditions']:
|
||||
if variable not in SENSOR_TYPES:
|
||||
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(ForeCastSensor(data, variable, unit))
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class ForeCastSensor(Entity):
|
||||
""" Implements an Forecast.io sensor. """
|
||||
|
||||
def __init__(self, weather_data, sensor_type, unit):
|
||||
self.client_name = 'Weather'
|
||||
self._name = SENSOR_TYPES[sensor_type][0]
|
||||
self.forecast_client = weather_data
|
||||
self._unit = unit
|
||||
self.type = sensor_type
|
||||
self._state = None
|
||||
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return '{} {}'.format(self.client_name, self._name)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
return self._unit_of_measurement
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def update(self):
|
||||
""" Gets the latest data from Forecast.io and updates the states. """
|
||||
|
||||
self.forecast_client.update()
|
||||
data = self.forecast_client.data
|
||||
|
||||
try:
|
||||
if self.type == 'summary':
|
||||
self._state = data.summary
|
||||
elif self.type == 'precip_intensity':
|
||||
if data.precipIntensity == 0:
|
||||
self._state = 'None'
|
||||
self._unit_of_measurement = ''
|
||||
else:
|
||||
self._state = data.precipIntensity
|
||||
elif self.type == 'precip_type':
|
||||
if data.precipType is None:
|
||||
self._state = 'None'
|
||||
self._unit_of_measurement = ''
|
||||
else:
|
||||
self._state = data.precipType
|
||||
elif self.type == 'dew_point':
|
||||
if self._unit == TEMP_CELCIUS:
|
||||
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':
|
||||
if self._unit == TEMP_CELCIUS:
|
||||
self._state = round(data.temperature, 1)
|
||||
elif self._unit == TEMP_FAHRENHEIT:
|
||||
self._state = round(data.temperature * 1.8 + 32.0, 1)
|
||||
else:
|
||||
self._state = round(data.temperature, 1)
|
||||
elif self.type == 'wind_speed':
|
||||
self._state = data.windSpeed
|
||||
elif self.type == 'wind_bearing':
|
||||
self._state = data.windBearing
|
||||
elif self.type == 'cloud_cover':
|
||||
self._state = round(data.cloudCover * 100, 1)
|
||||
elif self.type == 'humidity':
|
||||
self._state = round(data.humidity * 100, 1)
|
||||
elif self.type == 'pressure':
|
||||
self._state = round(data.pressure, 1)
|
||||
elif self.type == 'visibility':
|
||||
self._state = data.visibility
|
||||
elif self.type == 'ozone':
|
||||
self._state = round(data.ozone, 1)
|
||||
except forecastio.utils.PropertyUnavailable:
|
||||
pass
|
||||
|
||||
|
||||
class ForeCastData(object):
|
||||
""" Gets the latest data from Forecast.io. """
|
||||
|
||||
def __init__(self, api_key, latitude, longitude):
|
||||
self._api_key = api_key
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.data = None
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
""" Gets the latest data from Forecast.io. """
|
||||
|
||||
forecast = forecastio.load_forecast(self._api_key,
|
||||
self.latitude,
|
||||
self.longitude,
|
||||
units='si')
|
||||
self.data = forecast.currently()
|
||||
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
homeassistant.components.sensor.isy994
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for ISY994 sensors.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.isy994 import (ISY, ISYDeviceABC, SENSOR_STRING,
|
||||
HIDDEN_STRING)
|
||||
from homeassistant.const import (STATE_OPEN, STATE_CLOSED, STATE_HOME,
|
||||
STATE_NOT_HOME, STATE_ON, STATE_OFF)
|
||||
|
||||
DEFAULT_HIDDEN_WEATHER = ['Temperature_High', 'Temperature_Low', 'Feels_Like',
|
||||
'Temperature_Average', 'Pressure', 'Dew_Point',
|
||||
'Gust_Speed', 'Evapotranspiration',
|
||||
'Irrigation_Requirement', 'Water_Deficit_Yesterday',
|
||||
'Elevation', 'Average_Temperature_Tomorrow',
|
||||
'High_Temperature_Tomorrow',
|
||||
'Low_Temperature_Tomorrow', 'Humidity_Tomorrow',
|
||||
'Wind_Speed_Tomorrow', 'Gust_Speed_Tomorrow',
|
||||
'Rain_Tomorrow', 'Snow_Tomorrow',
|
||||
'Forecast_Average_Temperature',
|
||||
'Forecast_High_Temperature',
|
||||
'Forecast_Low_Temperature', 'Forecast_Humidity',
|
||||
'Forecast_Rain', 'Forecast_Snow']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the ISY994 platform. """
|
||||
# pylint: disable=protected-access
|
||||
logger = logging.getLogger(__name__)
|
||||
devs = []
|
||||
# verify connection
|
||||
if ISY is None or not ISY.connected:
|
||||
logger.error('A connection has not been made to the ISY controller.')
|
||||
return False
|
||||
|
||||
# import weather
|
||||
if ISY.climate is not None:
|
||||
for prop in ISY.climate._id2name:
|
||||
if prop is not None:
|
||||
prefix = HIDDEN_STRING \
|
||||
if prop in DEFAULT_HIDDEN_WEATHER else ''
|
||||
node = WeatherPseudoNode('ISY.weather.' + prop, prefix + prop,
|
||||
getattr(ISY.climate, prop),
|
||||
getattr(ISY.climate, prop + '_units'))
|
||||
devs.append(ISYSensorDevice(node))
|
||||
|
||||
# import sensor nodes
|
||||
for (path, node) in ISY.nodes:
|
||||
if SENSOR_STRING in node.name:
|
||||
if HIDDEN_STRING in path:
|
||||
node.name += HIDDEN_STRING
|
||||
devs.append(ISYSensorDevice(node, [STATE_ON, STATE_OFF]))
|
||||
|
||||
# import sensor programs
|
||||
for (folder_name, states) in (
|
||||
('HA.locations', [STATE_HOME, STATE_NOT_HOME]),
|
||||
('HA.sensors', [STATE_OPEN, STATE_CLOSED]),
|
||||
('HA.states', [STATE_ON, STATE_OFF])):
|
||||
try:
|
||||
folder = ISY.programs['My Programs'][folder_name]
|
||||
except KeyError:
|
||||
# folder does not exist
|
||||
pass
|
||||
else:
|
||||
for _, _, node_id in folder.children:
|
||||
node = folder[node_id].leaf
|
||||
devs.append(ISYSensorDevice(node, states))
|
||||
|
||||
add_devices(devs)
|
||||
|
||||
|
||||
class WeatherPseudoNode(object):
|
||||
""" This class allows weather variable to act as regular nodes. """
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
def __init__(self, device_id, name, status, units=None):
|
||||
self._id = device_id
|
||||
self.name = name
|
||||
self.status = status
|
||||
self.units = units
|
||||
|
||||
|
||||
class ISYSensorDevice(ISYDeviceABC):
|
||||
""" Represents a ISY sensor. """
|
||||
|
||||
_domain = 'sensor'
|
||||
|
||||
def __init__(self, node, states=None):
|
||||
super().__init__(node)
|
||||
self._states = states or []
|
||||
@@ -0,0 +1,167 @@
|
||||
"""
|
||||
homeassistant.components.modbus
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for Modbus sensors.
|
||||
|
||||
Configuration:
|
||||
|
||||
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 homeassistant.components.modbus as modbus
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import (
|
||||
TEMP_CELCIUS, TEMP_FAHRENHEIT,
|
||||
STATE_ON, STATE_OFF)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
DEPENDENCIES = ['modbus']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Read config and create Modbus devices. """
|
||||
sensors = []
|
||||
slave = config.get("slave", None)
|
||||
if modbus.TYPE == "serial" and not slave:
|
||||
_LOGGER.error("No slave number provided for serial Modbus")
|
||||
return False
|
||||
registers = config.get("registers")
|
||||
if registers:
|
||||
for regnum, register in registers.items():
|
||||
if register.get("name"):
|
||||
sensors.append(ModbusSensor(register.get("name"),
|
||||
slave,
|
||||
regnum,
|
||||
None,
|
||||
register.get("unit")))
|
||||
if register.get("bits"):
|
||||
bits = register.get("bits")
|
||||
for bitnum, bit in bits.items():
|
||||
if bit.get("name"):
|
||||
sensors.append(ModbusSensor(bit.get("name"),
|
||||
slave,
|
||||
regnum,
|
||||
bitnum))
|
||||
coils = config.get("coils")
|
||||
if coils:
|
||||
for coilnum, coil in coils.items():
|
||||
sensors.append(ModbusSensor(coil.get("name"),
|
||||
slave,
|
||||
coilnum,
|
||||
coil=True))
|
||||
|
||||
add_devices(sensors)
|
||||
|
||||
|
||||
class ModbusSensor(Entity):
|
||||
# pylint: disable=too-many-arguments
|
||||
""" Represents a Modbus Sensor. """
|
||||
|
||||
def __init__(self, name, slave, register, bit=None, unit=None, coil=False):
|
||||
self._name = name
|
||||
self.slave = int(slave) if slave else 1
|
||||
self.register = int(register)
|
||||
self.bit = int(bit) if bit else None
|
||||
self._value = None
|
||||
self._unit = unit
|
||||
self._coil = coil
|
||||
|
||||
def __str__(self):
|
||||
return "%s: %s" % (self.name, self.state)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""
|
||||
We should poll, because slaves are not allowed to
|
||||
initiate communication on Modbus networks.
|
||||
"""
|
||||
return True
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
""" Returns a unique id. """
|
||||
return "MODBUS-SENSOR-{}-{}-{}".format(self.slave,
|
||||
self.register,
|
||||
self.bit)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the sensor. """
|
||||
if self.bit:
|
||||
return STATE_ON if self._value else STATE_OFF
|
||||
else:
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Get the name of the sensor. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
if self._unit == "C":
|
||||
return TEMP_CELCIUS
|
||||
elif self._unit == "F":
|
||||
return TEMP_FAHRENHEIT
|
||||
else:
|
||||
return self._unit
|
||||
|
||||
def update(self):
|
||||
""" Update the state of the sensor. """
|
||||
if self._coil:
|
||||
result = modbus.NETWORK.read_coils(self.register, 1)
|
||||
self._value = result.bits[0]
|
||||
else:
|
||||
result = modbus.NETWORK.read_holding_registers(
|
||||
unit=self.slave, address=self.register,
|
||||
count=1)
|
||||
val = 0
|
||||
for i, res in enumerate(result.registers):
|
||||
val += res * (2**(i*16))
|
||||
if self.bit:
|
||||
self._value = val & (0x0001 << self.bit)
|
||||
else:
|
||||
self._value = val
|
||||
@@ -0,0 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
homeassistant.components.sensor.mqtt
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Allows to configure a MQTT sensor.
|
||||
|
||||
This generic sensor implementation uses the MQTT message payload
|
||||
as the sensor value. If messages in this state_topic are published
|
||||
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
|
||||
from homeassistant.helpers.entity import Entity
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = "MQTT Sensor"
|
||||
DEFAULT_QOS = 0
|
||||
|
||||
DEPENDENCIES = ['mqtt']
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Add MQTT Sensor """
|
||||
|
||||
if config.get('state_topic') is None:
|
||||
_LOGGER.error("Missing required variable: state_topic")
|
||||
return False
|
||||
|
||||
add_devices_callback([MqttSensor(
|
||||
hass,
|
||||
config.get('name', DEFAULT_NAME),
|
||||
config.get('state_topic'),
|
||||
config.get('qos', DEFAULT_QOS),
|
||||
config.get('unit_of_measurement'))])
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
class MqttSensor(Entity):
|
||||
""" Represents a sensor that can be updated using MQTT """
|
||||
def __init__(self, hass, name, state_topic, qos, unit_of_measurement):
|
||||
self._state = "-"
|
||||
self._hass = hass
|
||||
self._name = name
|
||||
self._state_topic = state_topic
|
||||
self._qos = qos
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
|
||||
def message_received(topic, payload, qos):
|
||||
""" A new MQTT message has been received. """
|
||||
self._state = payload
|
||||
self.update_ha_state()
|
||||
|
||||
mqtt.subscribe(hass, self._state_topic, message_received, self._qos)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" The name of the sensor """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit this state is expressed in. """
|
||||
return self._unit_of_measurement
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the entity. """
|
||||
return self._state
|
||||
@@ -0,0 +1,161 @@
|
||||
"""
|
||||
homeassistant.components.sensor.mysensors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for MySensors sensors.
|
||||
|
||||
Configuration:
|
||||
|
||||
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
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_LEVEL, EVENT_HOMEASSISTANT_STOP,
|
||||
TEMP_CELCIUS, TEMP_FAHRENHEIT,
|
||||
STATE_ON, STATE_OFF)
|
||||
|
||||
CONF_PORT = "port"
|
||||
CONF_DEBUG = "debug"
|
||||
CONF_PERSISTENCE = "persistence"
|
||||
|
||||
ATTR_NODE_ID = "node_id"
|
||||
ATTR_CHILD_ID = "child_id"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
REQUIREMENTS = ['https://github.com/theolind/pymysensors/archive/'
|
||||
'35b87d880147a34107da0d40cb815d75e6cb4af7.zip'
|
||||
'#pymysensors==0.2']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Setup the mysensors platform. """
|
||||
|
||||
import mysensors.mysensors as mysensors
|
||||
import mysensors.const_14 as const
|
||||
|
||||
devices = {} # keep track of devices added to HA
|
||||
# Just assume celcius means that the user wants metric for now.
|
||||
# It may make more sense to make this a global config option in the future.
|
||||
is_metric = (hass.config.temperature_unit == TEMP_CELCIUS)
|
||||
|
||||
def sensor_update(update_type, nid):
|
||||
""" Callback for sensor updates from the MySensors gateway. """
|
||||
_LOGGER.info("sensor_update %s: node %s", update_type, nid)
|
||||
sensor = gateway.sensors[nid]
|
||||
if sensor.sketch_name is None:
|
||||
return
|
||||
if nid not in devices:
|
||||
devices[nid] = {}
|
||||
|
||||
node = devices[nid]
|
||||
new_devices = []
|
||||
for child_id, child in sensor.children.items():
|
||||
if child_id not in node:
|
||||
node[child_id] = {}
|
||||
for value_type, value in child.values.items():
|
||||
if value_type not in node[child_id]:
|
||||
name = '{} {}.{}'.format(sensor.sketch_name, nid, child.id)
|
||||
node[child_id][value_type] = \
|
||||
MySensorsNodeValue(
|
||||
nid, child_id, name, value_type, is_metric, const)
|
||||
new_devices.append(node[child_id][value_type])
|
||||
else:
|
||||
node[child_id][value_type].update_sensor(
|
||||
value, sensor.battery_level)
|
||||
|
||||
if new_devices:
|
||||
_LOGGER.info("adding new devices: %s", new_devices)
|
||||
add_devices(new_devices)
|
||||
|
||||
port = config.get(CONF_PORT)
|
||||
if port is None:
|
||||
_LOGGER.error("Missing required key 'port'")
|
||||
return False
|
||||
|
||||
persistence = config.get(CONF_PERSISTENCE, True)
|
||||
|
||||
gateway = mysensors.SerialGateway(port, sensor_update,
|
||||
persistence=persistence)
|
||||
gateway.metric = is_metric
|
||||
gateway.debug = config.get(CONF_DEBUG, False)
|
||||
gateway.start()
|
||||
|
||||
if persistence:
|
||||
for nid in gateway.sensors:
|
||||
sensor_update('sensor_update', nid)
|
||||
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
||||
lambda event: gateway.stop())
|
||||
|
||||
|
||||
class MySensorsNodeValue(Entity):
|
||||
""" Represents the value of a MySensors child node. """
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
def __init__(self, node_id, child_id, name, value_type, metric, const):
|
||||
self._name = name
|
||||
self.node_id = node_id
|
||||
self.child_id = child_id
|
||||
self.battery_level = 0
|
||||
self.value_type = value_type
|
||||
self.metric = metric
|
||||
self._value = ''
|
||||
self.const = const
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" MySensor gateway pushes its state to HA. """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" The name of this sensor. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity. """
|
||||
if self.value_type == self.const.SetReq.V_TEMP:
|
||||
return TEMP_CELCIUS if self.metric else TEMP_FAHRENHEIT
|
||||
elif self.value_type == self.const.SetReq.V_HUM or \
|
||||
self.value_type == self.const.SetReq.V_DIMMER or \
|
||||
self.value_type == self.const.SetReq.V_LIGHT_LEVEL:
|
||||
return '%'
|
||||
return None
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
""" Returns the state attributes. """
|
||||
return {
|
||||
ATTR_NODE_ID: self.node_id,
|
||||
ATTR_CHILD_ID: self.child_id,
|
||||
ATTR_BATTERY_LEVEL: self.battery_level,
|
||||
}
|
||||
|
||||
def update_sensor(self, value, battery_level):
|
||||
""" Update a sensor with the latest value from the controller. """
|
||||
_LOGGER.info("%s value = %s", self._name, value)
|
||||
if self.value_type == self.const.SetReq.V_TRIPPED or \
|
||||
self.value_type == self.const.SetReq.V_ARMED:
|
||||
self._value = STATE_ON if int(value) == 1 else STATE_OFF
|
||||
else:
|
||||
self._value = value
|
||||
self.battery_level = battery_level
|
||||
self.update_ha_state()
|
||||
@@ -0,0 +1,208 @@
|
||||
"""
|
||||
homeassistant.components.sensor.openweathermap
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
OpenWeatherMap (OWM) service.
|
||||
|
||||
Configuration:
|
||||
|
||||
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
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS, TEMP_FAHRENHEIT)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['pyowm==2.2.1']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
SENSOR_TYPES = {
|
||||
'weather': ['Condition', ''],
|
||||
'temperature': ['Temperature', ''],
|
||||
'wind_speed': ['Wind speed', 'm/s'],
|
||||
'humidity': ['Humidity', '%'],
|
||||
'pressure': ['Pressure', 'hPa'],
|
||||
'clouds': ['Cloud coverage', '%'],
|
||||
'rain': ['Rain', 'mm'],
|
||||
'snow': ['Snow', 'mm']
|
||||
}
|
||||
|
||||
# Return cached results if last scan was less then this time ago
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Get the OpenWeatherMap sensor. """
|
||||
|
||||
if None in (hass.config.latitude, hass.config.longitude):
|
||||
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
||||
return False
|
||||
|
||||
try:
|
||||
from pyowm import OWM
|
||||
|
||||
except ImportError:
|
||||
_LOGGER.exception(
|
||||
"Unable to import pyowm. "
|
||||
"Did you maybe not install the 'PyOWM' package?")
|
||||
|
||||
return False
|
||||
|
||||
SENSOR_TYPES['temperature'][1] = hass.config.temperature_unit
|
||||
unit = hass.config.temperature_unit
|
||||
forecast = config.get('forecast', 0)
|
||||
owm = OWM(config.get(CONF_API_KEY, None))
|
||||
|
||||
if not owm:
|
||||
_LOGGER.error(
|
||||
"Connection error "
|
||||
"Please check your settings for OpenWeatherMap.")
|
||||
return None
|
||||
|
||||
data = WeatherData(owm, forecast, hass.config.latitude,
|
||||
hass.config.longitude)
|
||||
dev = []
|
||||
try:
|
||||
for variable in config['monitored_conditions']:
|
||||
if variable not in SENSOR_TYPES:
|
||||
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(OpenWeatherMapSensor(data, variable, unit))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if forecast == 1:
|
||||
SENSOR_TYPES['forecast'] = ['Forecast', '']
|
||||
dev.append(OpenWeatherMapSensor(data, 'forecast', unit))
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class OpenWeatherMapSensor(Entity):
|
||||
""" Implements an OpenWeatherMap sensor. """
|
||||
|
||||
def __init__(self, weather_data, sensor_type, temp_unit):
|
||||
self.client_name = 'Weather'
|
||||
self._name = SENSOR_TYPES[sensor_type][0]
|
||||
self.owa_client = weather_data
|
||||
self.temp_unit = temp_unit
|
||||
self.type = sensor_type
|
||||
self._state = None
|
||||
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return '{} {}'.format(self.client_name, self._name)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
return self._unit_of_measurement
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def update(self):
|
||||
""" Gets the latest data from OWM and updates the states. """
|
||||
|
||||
self.owa_client.update()
|
||||
data = self.owa_client.data
|
||||
fc_data = self.owa_client.fc_data
|
||||
|
||||
if self.type == 'weather':
|
||||
self._state = data.get_detailed_status()
|
||||
elif self.type == 'temperature':
|
||||
if self.temp_unit == TEMP_CELCIUS:
|
||||
self._state = round(data.get_temperature('celsius')['temp'],
|
||||
1)
|
||||
elif self.temp_unit == TEMP_FAHRENHEIT:
|
||||
self._state = round(data.get_temperature('fahrenheit')['temp'],
|
||||
1)
|
||||
else:
|
||||
self._state = round(data.get_temperature()['temp'], 1)
|
||||
elif self.type == 'wind_speed':
|
||||
self._state = data.get_wind()['speed']
|
||||
elif self.type == 'humidity':
|
||||
self._state = data.get_humidity()
|
||||
elif self.type == 'pressure':
|
||||
self._state = round(data.get_pressure()['press'], 0)
|
||||
elif self.type == 'clouds':
|
||||
self._state = data.get_clouds()
|
||||
elif self.type == 'rain':
|
||||
if data.get_rain():
|
||||
self._state = round(data.get_rain()['3h'], 0)
|
||||
self._unit_of_measurement = 'mm'
|
||||
else:
|
||||
self._state = 'not raining'
|
||||
self._unit_of_measurement = ''
|
||||
elif self.type == 'snow':
|
||||
if data.get_snow():
|
||||
self._state = round(data.get_snow(), 0)
|
||||
self._unit_of_measurement = 'mm'
|
||||
else:
|
||||
self._state = 'not snowing'
|
||||
self._unit_of_measurement = ''
|
||||
elif self.type == 'forecast':
|
||||
self._state = fc_data.get_weathers()[0].get_status()
|
||||
|
||||
|
||||
class WeatherData(object):
|
||||
""" Gets the latest data from OpenWeatherMap. """
|
||||
|
||||
def __init__(self, owm, forecast, latitude, longitude):
|
||||
self.owm = owm
|
||||
self.forecast = forecast
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.data = None
|
||||
self.fc_data = None
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
""" Gets the latest data from OpenWeatherMap. """
|
||||
obs = self.owm.weather_at_coords(self.latitude, self.longitude)
|
||||
self.data = obs.get_weather()
|
||||
|
||||
if self.forecast == 1:
|
||||
obs = self.owm.three_hours_forecast_at_coords(self.latitude,
|
||||
self.longitude)
|
||||
self.fc_data = obs.get_forecast()
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
homeassistant.components.sensor.rfxtrx
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Shows sensor values from RFXtrx sensors.
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the rfxtrx sensors you will need to add something like the following to
|
||||
your configuration.yaml file.
|
||||
|
||||
sensor:
|
||||
platform: rfxtrx
|
||||
device: PATH_TO_DEVICE
|
||||
|
||||
Variables:
|
||||
|
||||
device
|
||||
*Required
|
||||
Path to your RFXtrx device.
|
||||
E.g. /dev/serial/by-id/usb-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0
|
||||
"""
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from homeassistant.const import (TEMP_CELCIUS)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' +
|
||||
'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15']
|
||||
|
||||
DATA_TYPES = OrderedDict([
|
||||
('Temperature', TEMP_CELCIUS),
|
||||
('Humidity', '%'),
|
||||
('Barometer', ''),
|
||||
('Wind direction', ''),
|
||||
('Rain rate', '')])
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Setup the RFXtrx platform. """
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
sensors = {} # keep track of sensors added to HA
|
||||
|
||||
def sensor_update(event):
|
||||
""" Callback for sensor updates from the RFXtrx gateway. """
|
||||
if event.device.id_string in sensors:
|
||||
sensors[event.device.id_string].event = event
|
||||
else:
|
||||
logger.info("adding new sensor: %s", event.device.type_string)
|
||||
new_sensor = RfxtrxSensor(event)
|
||||
sensors[event.device.id_string] = new_sensor
|
||||
add_devices([new_sensor])
|
||||
try:
|
||||
import RFXtrx as rfxtrx
|
||||
except ImportError:
|
||||
logger.exception(
|
||||
"Failed to import rfxtrx")
|
||||
return False
|
||||
|
||||
device = config.get("device", "")
|
||||
rfxtrx.Core(device, sensor_update)
|
||||
|
||||
|
||||
class RfxtrxSensor(Entity):
|
||||
""" Represents a RFXtrx sensor. """
|
||||
|
||||
def __init__(self, event):
|
||||
self.event = event
|
||||
|
||||
self._unit_of_measurement = None
|
||||
self._data_type = None
|
||||
for data_type in DATA_TYPES:
|
||||
if data_type in self.event.values:
|
||||
self._unit_of_measurement = DATA_TYPES[data_type]
|
||||
self._data_type = data_type
|
||||
break
|
||||
|
||||
id_string = int(event.device.id_string.replace(":", ""), 16)
|
||||
self._name = "{} {} ({})".format(self._data_type,
|
||||
self.event.device.type_string,
|
||||
id_string)
|
||||
|
||||
def __str__(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
if self._data_type:
|
||||
return self.event.values[self._data_type]
|
||||
return None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Get the mame of the sensor. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
return self.event.values
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit this state is expressed in. """
|
||||
return self._unit_of_measurement
|
||||
@@ -0,0 +1,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
homeassistant.components.sensor.rpi_gpio
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Allows to configure a binary state sensor using RPi GPIO.
|
||||
Note: To use RPi GPIO, Home Assistant must be run as root.
|
||||
|
||||
sensor:
|
||||
platform: rpi_gpio
|
||||
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
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
try:
|
||||
import RPi.GPIO as GPIO
|
||||
except ImportError:
|
||||
GPIO = None
|
||||
from homeassistant.const import (DEVICE_DEFAULT_NAME,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
EVENT_HOMEASSISTANT_STOP)
|
||||
|
||||
DEFAULT_PULL_MODE = "UP"
|
||||
DEFAULT_VALUE_HIGH = "HIGH"
|
||||
DEFAULT_VALUE_LOW = "LOW"
|
||||
DEFAULT_BOUNCETIME = 50
|
||||
|
||||
REQUIREMENTS = ['RPi.GPIO==0.5.11']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the Raspberry PI GPIO ports. """
|
||||
if GPIO is None:
|
||||
_LOGGER.error('RPi.GPIO not available. rpi_gpio ports ignored.')
|
||||
return
|
||||
# pylint: disable=no-member
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
sensors = []
|
||||
pull_mode = config.get('pull_mode', DEFAULT_PULL_MODE)
|
||||
value_high = config.get('value_high', DEFAULT_VALUE_HIGH)
|
||||
value_low = config.get('value_low', DEFAULT_VALUE_LOW)
|
||||
bouncetime = config.get('bouncetime', DEFAULT_BOUNCETIME)
|
||||
ports = config.get('ports')
|
||||
for port_num, port_name in ports.items():
|
||||
sensors.append(RPiGPIOSensor(
|
||||
port_name, port_num, pull_mode,
|
||||
value_high, value_low, bouncetime))
|
||||
add_devices(sensors)
|
||||
|
||||
def cleanup_gpio(event):
|
||||
""" Stuff to do before stop home assistant. """
|
||||
# pylint: disable=no-member
|
||||
GPIO.cleanup()
|
||||
|
||||
def prepare_gpio(event):
|
||||
""" Stuff to do when home assistant starts. """
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, cleanup_gpio)
|
||||
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, prepare_gpio)
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
class RPiGPIOSensor(Entity):
|
||||
""" Sets up the Raspberry PI GPIO ports. """
|
||||
def __init__(self, port_name, port_num, pull_mode,
|
||||
value_high, value_low, bouncetime):
|
||||
# pylint: disable=no-member
|
||||
self._name = port_name or DEVICE_DEFAULT_NAME
|
||||
self._port = port_num
|
||||
self._pull = GPIO.PUD_DOWN if pull_mode == "DOWN" else GPIO.PUD_UP
|
||||
self._vhigh = value_high
|
||||
self._vlow = value_low
|
||||
self._bouncetime = bouncetime
|
||||
GPIO.setup(self._port, GPIO.IN, pull_up_down=self._pull)
|
||||
self._state = self._vhigh if GPIO.input(self._port) else self._vlow
|
||||
|
||||
def edge_callback(channel):
|
||||
""" port changed state """
|
||||
# pylint: disable=no-member
|
||||
self._state = self._vhigh if GPIO.input(channel) else self._vlow
|
||||
self.update_ha_state()
|
||||
|
||||
GPIO.add_event_detect(
|
||||
self._port,
|
||||
GPIO.BOTH,
|
||||
callback=edge_callback,
|
||||
bouncetime=self._bouncetime)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed. """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" The name of the sensor. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the entity. """
|
||||
return self._state
|
||||
@@ -1,13 +1,12 @@
|
||||
"""
|
||||
homeassistant.components.sensor.sabnzbd
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Monitors SABnzbd NZB client API
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the SABnzbd sensor you will need to add something like the following to
|
||||
your config/configuration.yaml
|
||||
your configuration.yaml file.
|
||||
|
||||
sensor:
|
||||
platform: sabnzbd
|
||||
@@ -22,18 +21,16 @@ sensor:
|
||||
- type: 'disk_size'
|
||||
- type: 'disk_free'
|
||||
|
||||
VARIABLES:
|
||||
Variables:
|
||||
|
||||
base_url
|
||||
*Required
|
||||
This is the base URL of your SABnzbd instance including the port number if not
|
||||
running on 80
|
||||
Example: http://192.168.1.32:8124/
|
||||
|
||||
running on 80, e.g. http://192.168.1.32:8124/
|
||||
|
||||
name
|
||||
*Optional
|
||||
The name to use when displaying this SABnzbd instance
|
||||
The name to use when displaying this SABnzbd instance.
|
||||
|
||||
monitored_variables
|
||||
*Required
|
||||
@@ -44,21 +41,19 @@ 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
|
||||
|
||||
|
||||
list of all available variables.
|
||||
"""
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
from homeassistant.external.nzbclients.sabnzbd import SabnzbdApi
|
||||
from homeassistant.external.nzbclients.sabnzbd import SabnzbdApiException
|
||||
|
||||
import logging
|
||||
|
||||
REQUIREMENTS = ['https://github.com/jamespcole/home-assistant-nzb-clients/'
|
||||
'archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip'
|
||||
'#python-sabnzbd==0.1']
|
||||
|
||||
SENSOR_TYPES = {
|
||||
'current_status': ['Status', ''],
|
||||
'speed': ['Speed', 'MB/s'],
|
||||
@@ -75,7 +70,9 @@ _THROTTLED_REFRESH = None
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the sensors """
|
||||
""" Sets up the SABnzbd sensors. """
|
||||
from pysabnzbd import SabnzbdApi, SabnzbdApiException
|
||||
|
||||
api_key = config.get("api_key")
|
||||
base_url = config.get("base_url")
|
||||
name = config.get("name", "SABnzbd")
|
||||
@@ -109,7 +106,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
|
||||
|
||||
class SabnzbdSensor(Entity):
|
||||
""" A Sabnzbd sensor """
|
||||
""" Represents an SABnzbd sensor. """
|
||||
|
||||
def __init__(self, sensor_type, sabnzb_client, client_name):
|
||||
self._name = SENSOR_TYPES[sensor_type][0]
|
||||
@@ -136,6 +133,7 @@ class SabnzbdSensor(Entity):
|
||||
def refresh_sabnzbd_data(self):
|
||||
""" Calls the throttled SABnzbd refresh method. """
|
||||
if _THROTTLED_REFRESH is not None:
|
||||
from pysabnzbd import SabnzbdApiException
|
||||
try:
|
||||
_THROTTLED_REFRESH()
|
||||
except SabnzbdApiException:
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
homeassistant.components.sensor.swiss_public_transport
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
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.
|
||||
|
||||
Configuration:
|
||||
|
||||
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
|
||||
from datetime import timedelta
|
||||
from requests import get
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_RESOURCE = 'http://transport.opendata.ch/v1/'
|
||||
|
||||
# Return cached results if last scan was less then this time ago
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Get the Swiss public transport sensor. """
|
||||
|
||||
# journal contains [0] Station ID start, [1] Station ID destination
|
||||
# [2] Station name start, and [3] Station name destination
|
||||
journey = [config.get('from'), config.get('to')]
|
||||
try:
|
||||
for location in [config.get('from', None), config.get('to', None)]:
|
||||
# transport.opendata.ch doesn't play nice with requests.Session
|
||||
result = get(_RESOURCE + 'locations?query=%s' % location)
|
||||
journey.append(result.json()['stations'][0]['name'])
|
||||
except KeyError:
|
||||
_LOGGER.exception(
|
||||
"Unable to determine stations. "
|
||||
"Check your settings and/or the availability of opendata.ch")
|
||||
|
||||
return None
|
||||
|
||||
dev = []
|
||||
data = PublicTransportData(journey)
|
||||
dev.append(SwissPublicTransportSensor(data, journey))
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class SwissPublicTransportSensor(Entity):
|
||||
""" Implements an Swiss public transport sensor. """
|
||||
|
||||
def __init__(self, data, journey):
|
||||
self.data = data
|
||||
self._name = '{}-{}'.format(journey[2], journey[3])
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def update(self):
|
||||
""" Gets the latest data from opendata.ch and updates the states. """
|
||||
times = self.data.update()
|
||||
try:
|
||||
self._state = ', '.join(times)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class PublicTransportData(object):
|
||||
""" Class for handling the data retrieval. """
|
||||
|
||||
def __init__(self, journey):
|
||||
self.start = journey[0]
|
||||
self.destination = journey[1]
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
""" Gets the latest data from opendata.ch. """
|
||||
|
||||
response = get(
|
||||
_RESOURCE +
|
||||
'connections?' +
|
||||
'from=' + self.start + '&' +
|
||||
'to=' + self.destination + '&' +
|
||||
'fields[]=connections/from/departureTimestamp/&' +
|
||||
'fields[]=connections/')
|
||||
|
||||
connections = response.json()['connections'][:2]
|
||||
|
||||
try:
|
||||
return [
|
||||
dt_util.datetime_to_time_str(
|
||||
dt_util.as_local(dt_util.utc_from_timestamp(
|
||||
item['from']['departureTimestamp']))
|
||||
)
|
||||
for item in connections
|
||||
]
|
||||
except KeyError:
|
||||
return ['n/a']
|
||||
@@ -1,17 +1,71 @@
|
||||
"""
|
||||
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:
|
||||
|
||||
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 psutil
|
||||
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import STATE_ON, STATE_OFF
|
||||
import psutil
|
||||
import logging
|
||||
|
||||
|
||||
REQUIREMENTS = ['psutil==3.0.0']
|
||||
SENSOR_TYPES = {
|
||||
'disk_use_percent': ['Disk Use', '%'],
|
||||
'disk_use': ['Disk Use', 'GiB'],
|
||||
@@ -21,6 +75,17 @@ SENSOR_TYPES = {
|
||||
'memory_free': ['RAM Free', 'MiB'],
|
||||
'processor_use': ['CPU Use', '%'],
|
||||
'process': ['Process', ''],
|
||||
'swap_use_percent': ['Swap Use', '%'],
|
||||
'swap_use': ['Swap Use', 'GiB'],
|
||||
'swap_free': ['Swap Free', 'GiB'],
|
||||
'network_out': ['Sent', 'MiB'],
|
||||
'network_in': ['Recieved', 'MiB'],
|
||||
'packets_out': ['Packets sent', ''],
|
||||
'packets_in': ['Packets recieved', ''],
|
||||
'ipv4_address': ['IPv4 address', ''],
|
||||
'ipv6_address': ['IPv6 address', ''],
|
||||
'last_boot': ['Last Boot', ''],
|
||||
'since_last_boot': ['Since Last Boot', '']
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -28,7 +93,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the sensors """
|
||||
""" Sets up the sensors. """
|
||||
|
||||
dev = []
|
||||
for resource in config['resources']:
|
||||
@@ -43,7 +108,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
|
||||
|
||||
class SystemMonitorSensor(Entity):
|
||||
""" A system monitor sensor """
|
||||
""" A system monitor sensor. """
|
||||
|
||||
def __init__(self, sensor_type, argument=''):
|
||||
self._name = SENSOR_TYPES[sensor_type][0] + ' ' + argument
|
||||
@@ -66,6 +131,7 @@ class SystemMonitorSensor(Entity):
|
||||
def unit_of_measurement(self):
|
||||
return self._unit_of_measurement
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def update(self):
|
||||
if self.type == 'disk_use_percent':
|
||||
self._state = psutil.disk_usage(self.argument).percent
|
||||
@@ -83,6 +149,12 @@ class SystemMonitorSensor(Entity):
|
||||
1024**2, 1)
|
||||
elif self.type == 'memory_free':
|
||||
self._state = round(psutil.virtual_memory().available / 1024**2, 1)
|
||||
elif self.type == 'swap_use_percent':
|
||||
self._state = psutil.swap_memory().percent
|
||||
elif self.type == 'swap_use':
|
||||
self._state = round(psutil.swap_memory().used / 1024**3, 1)
|
||||
elif self.type == 'swap_free':
|
||||
self._state = round(psutil.swap_memory().free / 1024**3, 1)
|
||||
elif self.type == 'processor_use':
|
||||
self._state = round(psutil.cpu_percent(interval=None))
|
||||
elif self.type == 'process':
|
||||
@@ -90,3 +162,24 @@ class SystemMonitorSensor(Entity):
|
||||
self._state = STATE_ON
|
||||
else:
|
||||
self._state = STATE_OFF
|
||||
elif self.type == 'network_out':
|
||||
self._state = round(psutil.net_io_counters(pernic=True)
|
||||
[self.argument][0] / 1024**2, 1)
|
||||
elif self.type == 'network_in':
|
||||
self._state = round(psutil.net_io_counters(pernic=True)
|
||||
[self.argument][1] / 1024**2, 1)
|
||||
elif self.type == 'packets_out':
|
||||
self._state = psutil.net_io_counters(pernic=True)[self.argument][2]
|
||||
elif self.type == 'packets_in':
|
||||
self._state = psutil.net_io_counters(pernic=True)[self.argument][3]
|
||||
elif self.type == 'ipv4_address':
|
||||
self._state = psutil.net_if_addrs()[self.argument][0][1]
|
||||
elif self.type == 'ipv6_address':
|
||||
self._state = psutil.net_if_addrs()[self.argument][1][1]
|
||||
elif self.type == 'last_boot':
|
||||
self._state = dt_util.datetime_to_date_str(
|
||||
dt_util.as_local(
|
||||
dt_util.utc_from_timestamp(psutil.boot_time())))
|
||||
elif self.type == 'since_last_boot':
|
||||
self._state = dt_util.utcnow() - dt_util.utc_from_timestamp(
|
||||
psutil.boot_time())
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"""
|
||||
homeassistant.components.sensor.tellstick
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Shows sensor values from tellstick sensors.
|
||||
Shows sensor values from Tellstick sensors.
|
||||
|
||||
Possible config keys:
|
||||
|
||||
@@ -35,6 +34,8 @@ import homeassistant.util as util
|
||||
|
||||
DatatypeDescription = namedtuple("DatatypeDescription", ['name', 'unit'])
|
||||
|
||||
REQUIREMENTS = ['tellcore-py==1.0.4']
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
@@ -77,7 +78,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
try:
|
||||
sensor_name = config[ts_sensor.id]
|
||||
except KeyError:
|
||||
if 'only_named' in config:
|
||||
if util.convert(config.get('only_named'), bool, False):
|
||||
continue
|
||||
sensor_name = str(ts_sensor.id)
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
homeassistant.components.sensor.temper
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for getting temperature from TEMPer devices.
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the temper sensors you will need to add something like the following to
|
||||
your configuration.yaml file.
|
||||
|
||||
sensor:
|
||||
platform: temper
|
||||
"""
|
||||
import logging
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import CONF_NAME, DEVICE_DEFAULT_NAME
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REQUIREMENTS = ['https://github.com/rkabadi/temper-python/archive/'
|
||||
'3dbdaf2d87b8db9a3cd6e5585fc704537dd2d09b.zip'
|
||||
'#temperusb==1.2.3']
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Find and return Temper sensors. """
|
||||
try:
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
from temperusb.temper import TemperHandler
|
||||
except ImportError:
|
||||
_LOGGER.error('Failed to import temperusb')
|
||||
return False
|
||||
|
||||
temp_unit = hass.config.temperature_unit
|
||||
name = config.get(CONF_NAME, DEVICE_DEFAULT_NAME)
|
||||
temper_devices = TemperHandler().get_devices()
|
||||
add_devices_callback([TemperSensor(dev, temp_unit, name + '_' + str(idx))
|
||||
for idx, dev in enumerate(temper_devices)])
|
||||
|
||||
|
||||
class TemperSensor(Entity):
|
||||
""" Represents an Temper temperature sensor. """
|
||||
def __init__(self, temper_device, temp_unit, name):
|
||||
self.temper_device = temper_device
|
||||
self.temp_unit = temp_unit
|
||||
self.current_value = None
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the temperature sensor. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the entity. """
|
||||
return self.current_value
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
return self.temp_unit
|
||||
|
||||
def update(self):
|
||||
""" Retrieve latest state. """
|
||||
try:
|
||||
self.current_value = self.temper_device.get_temperature()
|
||||
except IOError:
|
||||
_LOGGER.error('Failed to get temperature due to insufficient '
|
||||
'permissions. Try running with "sudo"')
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
homeassistant.components.sensor.time_date
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date and Time service.
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the Date and Time sensor you will need to add something like the
|
||||
following to your configuration.yaml file.
|
||||
|
||||
sensor:
|
||||
platform: time_date
|
||||
display_options:
|
||||
- 'time'
|
||||
- 'date'
|
||||
- 'date_time'
|
||||
- 'time_date'
|
||||
- 'time_utc'
|
||||
- 'beat'
|
||||
|
||||
Variables:
|
||||
|
||||
display_options
|
||||
*Required
|
||||
The variable you wish to display. See the configuration example above for a
|
||||
list of all available variables.
|
||||
"""
|
||||
import logging
|
||||
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
OPTION_TYPES = {
|
||||
'time': 'Time',
|
||||
'date': 'Date',
|
||||
'date_time': 'Date & Time',
|
||||
'time_date': 'Time & Date',
|
||||
'beat': 'Time (beat)',
|
||||
'time_utc': 'Time (UTC)',
|
||||
}
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Get the Time and Date sensor. """
|
||||
|
||||
if hass.config.time_zone is None:
|
||||
_LOGGER.error("Timezone is not set in Home Assistant config")
|
||||
return False
|
||||
|
||||
dev = []
|
||||
for variable in config['display_options']:
|
||||
if variable not in OPTION_TYPES:
|
||||
_LOGGER.error('Option type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(TimeDateSensor(variable))
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class TimeDateSensor(Entity):
|
||||
""" Implements a Time and Date sensor. """
|
||||
|
||||
def __init__(self, option_type):
|
||||
self._name = OPTION_TYPES[option_type]
|
||||
self.type = option_type
|
||||
self._state = None
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
def update(self):
|
||||
""" Gets the latest data and updates the states. """
|
||||
|
||||
time_date = dt_util.utcnow()
|
||||
time = dt_util.datetime_to_time_str(dt_util.as_local(time_date))
|
||||
time_utc = dt_util.datetime_to_time_str(time_date)
|
||||
date = dt_util.datetime_to_date_str(dt_util.as_local(time_date))
|
||||
|
||||
# Calculate the beat (Swatch Internet Time) time without date.
|
||||
hours, minutes, seconds = time_date.strftime('%H:%M:%S').split(':')
|
||||
beat = ((int(seconds) + (int(minutes) * 60) + ((int(hours) + 1) *
|
||||
3600)) / 86.4)
|
||||
|
||||
if self.type == 'time':
|
||||
self._state = time
|
||||
elif self.type == 'date':
|
||||
self._state = date
|
||||
elif self.type == 'date_time':
|
||||
self._state = date + ', ' + time
|
||||
elif self.type == 'time_date':
|
||||
self._state = time + ', ' + date
|
||||
elif self.type == 'time_utc':
|
||||
self._state = time_utc
|
||||
elif self.type == 'beat':
|
||||
self._state = '{0:.2f}'.format(beat)
|
||||
@@ -0,0 +1,175 @@
|
||||
"""
|
||||
homeassistant.components.sensor.transmission
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Monitors Transmission BitTorrent client API.
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the Transmission sensor you will need to add something like the
|
||||
following to your configuration.yaml file.
|
||||
|
||||
sensor:
|
||||
platform: transmission
|
||||
name: Transmission
|
||||
host: 192.168.1.26
|
||||
port: 9091
|
||||
username: YOUR_USERNAME
|
||||
password: YOUR_PASSWORD
|
||||
monitored_variables:
|
||||
- 'current_status'
|
||||
- 'download_speed'
|
||||
- 'upload_speed'
|
||||
|
||||
Variables:
|
||||
|
||||
host
|
||||
*Required
|
||||
This is the IP address of your Transmission daemon, e.g. 192.168.1.32
|
||||
|
||||
port
|
||||
*Optional
|
||||
The port your Transmission daemon uses, defaults to 9091. Example: 8080
|
||||
|
||||
username
|
||||
*Required
|
||||
Your Transmission username.
|
||||
|
||||
password
|
||||
*Required
|
||||
Your Transmission password.
|
||||
|
||||
name
|
||||
*Optional
|
||||
The name to use when displaying this Transmission instance.
|
||||
|
||||
monitored_variables
|
||||
*Required
|
||||
Variables to monitor. See the configuration example above for a
|
||||
list of all available variables to monitor.
|
||||
"""
|
||||
from homeassistant.util import Throttle
|
||||
from datetime import timedelta
|
||||
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
import transmissionrpc
|
||||
|
||||
from transmissionrpc.error import TransmissionError
|
||||
|
||||
import logging
|
||||
|
||||
REQUIREMENTS = ['transmissionrpc==0.11']
|
||||
SENSOR_TYPES = {
|
||||
'current_status': ['Status', ''],
|
||||
'download_speed': ['Down Speed', 'MB/s'],
|
||||
'upload_speed': ['Up Speed', 'MB/s']
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
_THROTTLED_REFRESH = None
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the Transmission sensors. """
|
||||
host = config.get(CONF_HOST)
|
||||
username = config.get(CONF_USERNAME, None)
|
||||
password = config.get(CONF_PASSWORD, None)
|
||||
port = config.get('port', 9091)
|
||||
|
||||
name = config.get("name", "Transmission")
|
||||
if not host:
|
||||
_LOGGER.error('Missing config variable %s', CONF_HOST)
|
||||
return False
|
||||
|
||||
# import logging
|
||||
# logging.getLogger('transmissionrpc').setLevel(logging.DEBUG)
|
||||
|
||||
transmission_api = transmissionrpc.Client(
|
||||
host, port=port, user=username, password=password)
|
||||
try:
|
||||
transmission_api.session_stats()
|
||||
except TransmissionError:
|
||||
_LOGGER.exception("Connection to Transmission API failed.")
|
||||
return False
|
||||
|
||||
# pylint: disable=global-statement
|
||||
global _THROTTLED_REFRESH
|
||||
_THROTTLED_REFRESH = Throttle(timedelta(seconds=1))(
|
||||
transmission_api.session_stats)
|
||||
|
||||
dev = []
|
||||
for variable in config['monitored_variables']:
|
||||
if variable not in SENSOR_TYPES:
|
||||
_LOGGER.error('Sensor type: "%s" does not exist', variable)
|
||||
else:
|
||||
dev.append(TransmissionSensor(
|
||||
variable, transmission_api, name))
|
||||
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
class TransmissionSensor(Entity):
|
||||
""" A Transmission sensor. """
|
||||
|
||||
def __init__(self, sensor_type, transmission_client, client_name):
|
||||
self._name = SENSOR_TYPES[sensor_type][0]
|
||||
self.transmission_client = transmission_client
|
||||
self.type = sensor_type
|
||||
self.client_name = client_name
|
||||
self._state = None
|
||||
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.client_name + ' ' + self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
return self._unit_of_measurement
|
||||
|
||||
def refresh_transmission_data(self):
|
||||
""" Calls the throttled Transmission refresh method. """
|
||||
if _THROTTLED_REFRESH is not None:
|
||||
try:
|
||||
_THROTTLED_REFRESH()
|
||||
except TransmissionError:
|
||||
_LOGGER.exception(
|
||||
self.name + " Connection to Transmission API failed."
|
||||
)
|
||||
|
||||
def update(self):
|
||||
""" Gets the latest data from Transmission and updates the state. """
|
||||
self.refresh_transmission_data()
|
||||
if self.type == 'current_status':
|
||||
if self.transmission_client.session:
|
||||
upload = self.transmission_client.session.uploadSpeed
|
||||
download = self.transmission_client.session.downloadSpeed
|
||||
if upload > 0 and download > 0:
|
||||
self._state = 'Up/Down'
|
||||
elif upload > 0 and download == 0:
|
||||
self._state = 'Seeding'
|
||||
elif upload == 0 and download > 0:
|
||||
self._state = 'Downloading'
|
||||
else:
|
||||
self._state = 'Idle'
|
||||
else:
|
||||
self._state = 'Unknown'
|
||||
|
||||
if self.transmission_client.session:
|
||||
if self.type == 'download_speed':
|
||||
mb_spd = float(self.transmission_client.session.downloadSpeed)
|
||||
mb_spd = mb_spd / 1024 / 1024
|
||||
self._state = round(mb_spd, 2 if mb_spd < 0.1 else 1)
|
||||
elif self.type == 'upload_speed':
|
||||
mb_spd = float(self.transmission_client.session.uploadSpeed)
|
||||
mb_spd = mb_spd / 1024 / 1024
|
||||
self._state = round(mb_spd, 2 if mb_spd < 0.1 else 1)
|
||||
@@ -1,9 +1,12 @@
|
||||
"""
|
||||
homeassistant.components.sensor.vera
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for Vera sensors.
|
||||
|
||||
Configuration:
|
||||
|
||||
To use the Vera sensors you will need to add something like the following to
|
||||
your config/configuration.yaml
|
||||
your configuration.yaml file.
|
||||
|
||||
sensor:
|
||||
platform: vera
|
||||
@@ -15,13 +18,12 @@ sensor:
|
||||
13:
|
||||
name: Another sensor
|
||||
|
||||
VARIABLES:
|
||||
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/
|
||||
running on 80, e.g. http://192.168.1.21:3480/
|
||||
|
||||
|
||||
device_data
|
||||
@@ -29,7 +31,7 @@ device_data
|
||||
This contains an array additional device info for your Vera devices. It is not
|
||||
required and if not specified all sensors 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
|
||||
as the key for the device within device_data.
|
||||
|
||||
These are the variables for the device_data array:
|
||||
|
||||
@@ -37,24 +39,25 @@ 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
|
||||
|
||||
will be used.
|
||||
|
||||
exclude
|
||||
*Optional
|
||||
This parameter allows you to exclude the specified device from homeassistant,
|
||||
it should be set to "true" if you want this device excluded
|
||||
|
||||
This parameter allows you to exclude the specified device from Home Assistant,
|
||||
it should be set to "true" if you want this device excluded.
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
from requests.exceptions import RequestException
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME)
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
import homeassistant.external.vera.vera as veraApi
|
||||
ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME,
|
||||
TEMP_CELCIUS, TEMP_FAHRENHEIT)
|
||||
|
||||
REQUIREMENTS = ['https://github.com/balloob/home-assistant-vera-api/archive/'
|
||||
'a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip'
|
||||
'#python-vera==0.1']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -62,6 +65,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
# pylint: disable=unused-argument
|
||||
def get_devices(hass, config):
|
||||
""" Find and return Vera Sensors. """
|
||||
import pyvera as veraApi
|
||||
|
||||
base_url = config.get('vera_controller_url')
|
||||
if not base_url:
|
||||
@@ -95,12 +99,12 @@ def get_devices(hass, config):
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Performs setup for Vera controller devices """
|
||||
""" Performs setup for Vera controller devices. """
|
||||
add_devices(get_devices(hass, config))
|
||||
|
||||
|
||||
class VeraSensor(Entity):
|
||||
""" Represents a Vera Sensor """
|
||||
""" Represents a Vera Sensor. """
|
||||
|
||||
def __init__(self, vera_device, extra_data=None):
|
||||
self.vera_device = vera_device
|
||||
@@ -110,6 +114,7 @@ class VeraSensor(Entity):
|
||||
else:
|
||||
self._name = self.vera_device.name
|
||||
self.current_value = ''
|
||||
self._temperature_units = None
|
||||
|
||||
def __str__(self):
|
||||
return "%s %s %s" % (self.name, self.vera_device.deviceId, self.state)
|
||||
@@ -123,6 +128,11 @@ class VeraSensor(Entity):
|
||||
""" Get the mame of the sensor. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity, if any. """
|
||||
return self._temperature_units
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
attr = super().state_attributes
|
||||
@@ -135,11 +145,12 @@ class VeraSensor(Entity):
|
||||
|
||||
if self.vera_device.is_trippable:
|
||||
last_tripped = self.vera_device.refresh_value('LastTrip')
|
||||
trip_time_str = time.strftime(
|
||||
"%Y-%m-%d %H:%M",
|
||||
time.localtime(int(last_tripped))
|
||||
)
|
||||
attr[ATTR_LAST_TRIP_TIME] = trip_time_str
|
||||
if last_tripped is not None:
|
||||
utc_time = dt_util.utc_from_timestamp(int(last_tripped))
|
||||
attr[ATTR_LAST_TRIP_TIME] = dt_util.datetime_to_str(
|
||||
utc_time)
|
||||
else:
|
||||
attr[ATTR_LAST_TRIP_TIME] = None
|
||||
tripped = self.vera_device.refresh_value('Tripped')
|
||||
attr[ATTR_TRIPPED] = 'True' if tripped == '1' else 'False'
|
||||
|
||||
@@ -151,7 +162,20 @@ class VeraSensor(Entity):
|
||||
self.vera_device.refresh_value('CurrentTemperature')
|
||||
current_temp = self.vera_device.get_value('CurrentTemperature')
|
||||
vera_temp_units = self.vera_device.veraController.temperature_units
|
||||
self.current_value = current_temp + '°' + vera_temp_units
|
||||
|
||||
if vera_temp_units == 'F':
|
||||
self._temperature_units = TEMP_FAHRENHEIT
|
||||
else:
|
||||
self._temperature_units = TEMP_CELCIUS
|
||||
|
||||
if self.hass:
|
||||
temp = self.hass.config.temperature(
|
||||
current_temp,
|
||||
self._temperature_units)
|
||||
|
||||
current_temp, self._temperature_units = temp
|
||||
|
||||
self.current_value = current_temp
|
||||
elif self.vera_device.category == "Light Sensor":
|
||||
self.vera_device.refresh_value('CurrentLevel')
|
||||
self.current_value = self.vera_device.get_value('CurrentLevel')
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
homeassistant.components.sensor.verisure
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Interfaces with Verisure sensors.
|
||||
"""
|
||||
import logging
|
||||
|
||||
import homeassistant.components.verisure as verisure
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import TEMP_CELCIUS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the Verisure platform. """
|
||||
|
||||
if not verisure.MY_PAGES:
|
||||
_LOGGER.error('A connection has not been made to Verisure mypages.')
|
||||
return False
|
||||
|
||||
sensors = []
|
||||
|
||||
sensors.extend([
|
||||
VerisureThermometer(value)
|
||||
for value in verisure.get_climate_status().values()
|
||||
if verisure.SHOW_THERMOMETERS and
|
||||
hasattr(value, 'temperature') and value.temperature
|
||||
])
|
||||
|
||||
sensors.extend([
|
||||
VerisureHygrometer(value)
|
||||
for value in verisure.get_climate_status().values()
|
||||
if verisure.SHOW_HYGROMETERS and
|
||||
hasattr(value, 'humidity') and value.humidity
|
||||
])
|
||||
|
||||
sensors.extend([
|
||||
VerisureAlarm(value)
|
||||
for value in verisure.get_alarm_status().values()
|
||||
if verisure.SHOW_ALARM
|
||||
])
|
||||
|
||||
add_devices(sensors)
|
||||
|
||||
|
||||
class VerisureThermometer(Entity):
|
||||
""" represents a Verisure thermometer within home assistant. """
|
||||
|
||||
def __init__(self, climate_status):
|
||||
self._id = climate_status.id
|
||||
self._device = verisure.MY_PAGES.DEVICE_CLIMATE
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device. """
|
||||
return '{} {}'.format(
|
||||
verisure.STATUS[self._device][self._id].location,
|
||||
"Temperature")
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
# remove ° character
|
||||
return verisure.STATUS[self._device][self._id].temperature[:-1]
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity """
|
||||
return TEMP_CELCIUS # can verisure report in fahrenheit?
|
||||
|
||||
def update(self):
|
||||
''' update sensor '''
|
||||
verisure.update()
|
||||
|
||||
|
||||
class VerisureHygrometer(Entity):
|
||||
""" represents a Verisure hygrometer within home assistant. """
|
||||
|
||||
def __init__(self, climate_status):
|
||||
self._id = climate_status.id
|
||||
self._device = verisure.MY_PAGES.DEVICE_CLIMATE
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device. """
|
||||
return '{} {}'.format(
|
||||
verisure.STATUS[self._device][self._id].location,
|
||||
"Humidity")
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
# remove % character
|
||||
return verisure.STATUS[self._device][self._id].humidity[:-1]
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
""" Unit of measurement of this entity """
|
||||
return "%"
|
||||
|
||||
def update(self):
|
||||
''' update sensor '''
|
||||
verisure.update()
|
||||
|
||||
|
||||
class VerisureAlarm(Entity):
|
||||
""" represents a Verisure alarm status within home assistant. """
|
||||
|
||||
def __init__(self, alarm_status):
|
||||
self._id = alarm_status.id
|
||||
self._device = verisure.MY_PAGES.DEVICE_ALARM
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device. """
|
||||
return 'Alarm {}'.format(self._id)
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
""" Returns the state of the device. """
|
||||
return verisure.STATUS[self._device][self._id].label
|
||||
|
||||
def update(self):
|
||||
''' update sensor '''
|
||||
verisure.update()
|
||||
@@ -1,15 +1,22 @@
|
||||
""" Support for Wink sensors. """
|
||||
"""
|
||||
homeassistant.components.sensor.wink
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for Wink sensors.
|
||||
"""
|
||||
import logging
|
||||
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
import homeassistant.external.wink.pywink as pywink
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, STATE_OPEN, STATE_CLOSED
|
||||
|
||||
REQUIREMENTS = ['https://github.com/balloob/python-wink/archive/'
|
||||
'c2b700e8ca866159566ecf5e644d9c297f69f257.zip'
|
||||
'#python-wink==0.1']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the Wink platform. """
|
||||
import pywink
|
||||
|
||||
if discovery_info is None:
|
||||
token = config.get(CONF_ACCESS_TOKEN)
|
||||
|
||||
@@ -25,7 +32,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
|
||||
|
||||
class WinkSensorDevice(Entity):
|
||||
""" represents a wink sensor within home assistant. """
|
||||
""" Represents a wink sensor. """
|
||||
|
||||
def __init__(self, wink):
|
||||
self.wink = wink
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""
|
||||
homeassistant.components.sensor.zwave
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Interfaces with Z-Wave sensors.
|
||||
"""
|
||||
# pylint: disable=import-error
|
||||
|
||||
Reference in New Issue
Block a user