mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Merge branch 'wink-sensors' into dev
This commit is contained in:
commit
c224b91945
@ -27,7 +27,7 @@ import homeassistant.util as util
|
|||||||
DOMAIN = "homeassistant"
|
DOMAIN = "homeassistant"
|
||||||
|
|
||||||
# How often time_changed event should fire
|
# How often time_changed event should fire
|
||||||
TIMER_INTERVAL = 10 # seconds
|
TIMER_INTERVAL = 3 # seconds
|
||||||
|
|
||||||
# How long we wait for the result of a service call
|
# How long we wait for the result of a service call
|
||||||
SERVICE_CALL_LIMIT = 10 # seconds
|
SERVICE_CALL_LIMIT = 10 # seconds
|
||||||
|
@ -111,7 +111,8 @@ class DeviceTracker(object):
|
|||||||
""" Triggers update of the device states. """
|
""" Triggers update of the device states. """
|
||||||
self.update_devices(now)
|
self.update_devices(now)
|
||||||
|
|
||||||
dev_group = group.Group(hass, GROUP_NAME_ALL_DEVICES)
|
dev_group = group.Group(
|
||||||
|
hass, GROUP_NAME_ALL_DEVICES, user_defined=False)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def reload_known_devices_service(service):
|
def reload_known_devices_service(service):
|
||||||
|
@ -59,6 +59,9 @@
|
|||||||
case "thermostat":
|
case "thermostat":
|
||||||
return "homeassistant:thermostat";
|
return "homeassistant:thermostat";
|
||||||
|
|
||||||
|
case "sensor":
|
||||||
|
return "visibility";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "bookmark-outline";
|
return "bookmark-outline";
|
||||||
}
|
}
|
||||||
|
89
homeassistant/components/sensor/__init__.py
Normal file
89
homeassistant/components/sensor/__init__.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.sensor
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Component to interface with various sensors that can be monitored.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
import homeassistant.util as util
|
||||||
|
from homeassistant.const import (
|
||||||
|
STATE_OPEN)
|
||||||
|
from homeassistant.helpers import (
|
||||||
|
platform_devices_from_config)
|
||||||
|
from homeassistant.components import group, discovery, wink
|
||||||
|
|
||||||
|
DOMAIN = 'sensor'
|
||||||
|
DEPENDENCIES = []
|
||||||
|
|
||||||
|
GROUP_NAME_ALL_SENSORS = 'all_sensors'
|
||||||
|
ENTITY_ID_ALL_SENSORS = group.ENTITY_ID_FORMAT.format(
|
||||||
|
GROUP_NAME_ALL_SENSORS)
|
||||||
|
|
||||||
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
|
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=1)
|
||||||
|
|
||||||
|
# Maps discovered services to their platforms
|
||||||
|
DISCOVERY_PLATFORMS = {
|
||||||
|
wink.DISCOVER_SENSORS: 'wink',
|
||||||
|
}
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def is_on(hass, entity_id=None):
|
||||||
|
""" Returns if the sensor is open based on the statemachine. """
|
||||||
|
entity_id = entity_id or ENTITY_ID_ALL_SENSORS
|
||||||
|
|
||||||
|
return hass.states.is_state(entity_id, STATE_OPEN)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
""" Track states and offer events for sensors. """
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
sensors = platform_devices_from_config(
|
||||||
|
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
|
def update_sensor_states(now):
|
||||||
|
""" Update states of all sensors. """
|
||||||
|
if sensors:
|
||||||
|
logger.info("Updating sensor states")
|
||||||
|
|
||||||
|
for sensor in sensors.values():
|
||||||
|
sensor.update_ha_state(hass, True)
|
||||||
|
|
||||||
|
update_sensor_states(None)
|
||||||
|
|
||||||
|
# Track all sensors in a group
|
||||||
|
sensor_group = group.Group(
|
||||||
|
hass, GROUP_NAME_ALL_SENSORS, sensors.keys(), False)
|
||||||
|
|
||||||
|
def sensor_discovered(service, info):
|
||||||
|
""" Called when a sensor is discovered. """
|
||||||
|
platform = get_component("{}.{}".format(
|
||||||
|
DOMAIN, DISCOVERY_PLATFORMS[service]))
|
||||||
|
|
||||||
|
discovered = platform.devices_discovered(hass, config, info)
|
||||||
|
|
||||||
|
for sensor in discovered:
|
||||||
|
if sensor is not None and sensor not in sensors.values():
|
||||||
|
sensor.entity_id = util.ensure_unique_string(
|
||||||
|
ENTITY_ID_FORMAT.format(util.slugify(sensor.name)),
|
||||||
|
sensors.keys())
|
||||||
|
|
||||||
|
sensors[sensor.entity_id] = sensor
|
||||||
|
|
||||||
|
sensor.update_ha_state(hass)
|
||||||
|
|
||||||
|
sensor_group.update_tracked_entity_ids(sensors.keys())
|
||||||
|
|
||||||
|
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), sensor_discovered)
|
||||||
|
|
||||||
|
hass.track_time_change(update_sensor_states)
|
||||||
|
|
||||||
|
return True
|
35
homeassistant/components/sensor/wink.py
Normal file
35
homeassistant/components/sensor/wink.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
""" Support for Wink sensors. """
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# pylint: disable=no-name-in-module, import-error
|
||||||
|
import homeassistant.external.wink.pywink as pywink
|
||||||
|
|
||||||
|
from homeassistant.components.wink import WinkSensorDevice
|
||||||
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def get_devices(hass, config):
|
||||||
|
""" Find and return Wink sensors. """
|
||||||
|
token = config.get(CONF_ACCESS_TOKEN)
|
||||||
|
|
||||||
|
if token is None:
|
||||||
|
logging.getLogger(__name__).error(
|
||||||
|
"Missing wink access_token - "
|
||||||
|
"get one at https://winkbearertoken.appspot.com/")
|
||||||
|
return []
|
||||||
|
|
||||||
|
pywink.set_bearer_token(token)
|
||||||
|
|
||||||
|
return get_sensors()
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def devices_discovered(hass, config, info):
|
||||||
|
""" Called when a device is discovered. """
|
||||||
|
return get_sensors()
|
||||||
|
|
||||||
|
|
||||||
|
def get_sensors():
|
||||||
|
""" Returns the Wink sensors. """
|
||||||
|
return [WinkSensorDevice(sensor) for sensor in pywink.get_sensors()]
|
@ -8,9 +8,10 @@ import homeassistant.external.wink.pywink as pywink
|
|||||||
|
|
||||||
from homeassistant import bootstrap
|
from homeassistant import bootstrap
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.loader import get_component
|
||||||
from homeassistant.helpers import validate_config, ToggleDevice
|
from homeassistant.helpers import validate_config, ToggleDevice, Device
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN,
|
EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN,
|
||||||
|
STATE_OPEN, STATE_CLOSED,
|
||||||
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
|
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
|
||||||
|
|
||||||
DOMAIN = "wink"
|
DOMAIN = "wink"
|
||||||
@ -18,6 +19,7 @@ DEPENDENCIES = []
|
|||||||
|
|
||||||
DISCOVER_LIGHTS = "wink.lights"
|
DISCOVER_LIGHTS = "wink.lights"
|
||||||
DISCOVER_SWITCHES = "wink.switches"
|
DISCOVER_SWITCHES = "wink.switches"
|
||||||
|
DISCOVER_SENSORS = "wink.sensors"
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
@ -32,7 +34,8 @@ def setup(hass, config):
|
|||||||
# Load components for the devices in the Wink that we support
|
# Load components for the devices in the Wink that we support
|
||||||
for component_name, func_exists, discovery_type in (
|
for component_name, func_exists, discovery_type in (
|
||||||
('light', pywink.get_bulbs, DISCOVER_LIGHTS),
|
('light', pywink.get_bulbs, DISCOVER_LIGHTS),
|
||||||
('switch', pywink.get_switches, DISCOVER_SWITCHES)):
|
('switch', pywink.get_switches, DISCOVER_SWITCHES),
|
||||||
|
('sensor', pywink.get_sensors, DISCOVER_SENSORS)):
|
||||||
|
|
||||||
if func_exists():
|
if func_exists():
|
||||||
component = get_component(component_name)
|
component = get_component(component_name)
|
||||||
@ -50,6 +53,44 @@ def setup(hass, config):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class WinkSensorDevice(Device):
|
||||||
|
""" represents a wink sensor within home assistant. """
|
||||||
|
|
||||||
|
def __init__(self, wink):
|
||||||
|
self.wink = wink
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
""" Returns the state. """
|
||||||
|
return STATE_OPEN if self.is_open else STATE_CLOSED
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
""" Returns the id of this wink switch """
|
||||||
|
return "{}.{}".format(self.__class__, self.wink.deviceId())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the sensor if any. """
|
||||||
|
return self.wink.name()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
""" Returns optional state attributes. """
|
||||||
|
return {
|
||||||
|
ATTR_FRIENDLY_NAME: self.wink.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
""" Update state of the sensor. """
|
||||||
|
self.wink.updateState()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_open(self):
|
||||||
|
""" True if door is open. """
|
||||||
|
return self.wink.state()
|
||||||
|
|
||||||
|
|
||||||
class WinkToggleDevice(ToggleDevice):
|
class WinkToggleDevice(ToggleDevice):
|
||||||
""" represents a WeMo switch within home assistant. """
|
""" represents a WeMo switch within home assistant. """
|
||||||
|
|
||||||
|
@ -35,6 +35,8 @@ STATE_OFF = 'off'
|
|||||||
STATE_HOME = 'home'
|
STATE_HOME = 'home'
|
||||||
STATE_NOT_HOME = 'not_home'
|
STATE_NOT_HOME = 'not_home'
|
||||||
STATE_UNKNOWN = "unknown"
|
STATE_UNKNOWN = "unknown"
|
||||||
|
STATE_OPEN = 'open'
|
||||||
|
STATE_CLOSED = 'closed'
|
||||||
|
|
||||||
# #### STATE AND EVENT ATTRIBUTES ####
|
# #### STATE AND EVENT ATTRIBUTES ####
|
||||||
# Contains current time for a TIME_CHANGED event
|
# Contains current time for a TIME_CHANGED event
|
||||||
|
163
homeassistant/external/wink/pywink.py
vendored
163
homeassistant/external/wink/pywink.py
vendored
@ -10,6 +10,124 @@ baseUrl = "https://winkapi.quirky.com"
|
|||||||
headers = {}
|
headers = {}
|
||||||
|
|
||||||
|
|
||||||
|
class wink_sensor_pod(object):
|
||||||
|
""" represents a wink.py sensor
|
||||||
|
json_obj holds the json stat at init (and if there is a refresh it's updated
|
||||||
|
it's the native format for this objects methods
|
||||||
|
and looks like so:
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"last_event": {
|
||||||
|
"brightness_occurred_at": None,
|
||||||
|
"loudness_occurred_at": None,
|
||||||
|
"vibration_occurred_at": None
|
||||||
|
},
|
||||||
|
"model_name": "Tripper",
|
||||||
|
"capabilities": {
|
||||||
|
"sensor_types": [
|
||||||
|
{
|
||||||
|
"field": "opened",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "battery",
|
||||||
|
"type": "percentage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"manufacturer_device_model": "quirky_ge_tripper",
|
||||||
|
"location": "",
|
||||||
|
"radio_type": "zigbee",
|
||||||
|
"manufacturer_device_id": None,
|
||||||
|
"gang_id": None,
|
||||||
|
"sensor_pod_id": "37614",
|
||||||
|
"subscription": {
|
||||||
|
},
|
||||||
|
"units": {
|
||||||
|
},
|
||||||
|
"upc_id": "184",
|
||||||
|
"hidden_at": None,
|
||||||
|
"last_reading": {
|
||||||
|
"battery_voltage_threshold_2": 0,
|
||||||
|
"opened": False,
|
||||||
|
"battery_alarm_mask": 0,
|
||||||
|
"opened_updated_at": 1421697092.7347496,
|
||||||
|
"battery_voltage_min_threshold_updated_at": 1421697092.7347229,
|
||||||
|
"battery_voltage_min_threshold": 0,
|
||||||
|
"connection": None,
|
||||||
|
"battery_voltage": 25,
|
||||||
|
"battery_voltage_threshold_1": 25,
|
||||||
|
"connection_updated_at": None,
|
||||||
|
"battery_voltage_threshold_3": 0,
|
||||||
|
"battery_voltage_updated_at": 1421697092.7347066,
|
||||||
|
"battery_voltage_threshold_1_updated_at": 1421697092.7347302,
|
||||||
|
"battery_voltage_threshold_3_updated_at": 1421697092.7347434,
|
||||||
|
"battery_voltage_threshold_2_updated_at": 1421697092.7347374,
|
||||||
|
"battery": 1.0,
|
||||||
|
"battery_updated_at": 1421697092.7347553,
|
||||||
|
"battery_alarm_mask_updated_at": 1421697092.734716
|
||||||
|
},
|
||||||
|
"triggers": [
|
||||||
|
],
|
||||||
|
"name": "MasterBathroom",
|
||||||
|
"lat_lng": [
|
||||||
|
37.550773,
|
||||||
|
-122.279182
|
||||||
|
],
|
||||||
|
"uuid": "a2cb868a-dda3-4211-ab73-fc08087aeed7",
|
||||||
|
"locale": "en_us",
|
||||||
|
"device_manufacturer": "quirky_ge",
|
||||||
|
"created_at": 1421523277,
|
||||||
|
"local_id": "2",
|
||||||
|
"hub_id": "88264"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, aJSonObj, objectprefix="sensor_pods"):
|
||||||
|
self.jsonState = aJSonObj
|
||||||
|
self.objectprefix = objectprefix
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s %s %s" % (self.name(), self.deviceId(), self.state())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Wink sensor %s %s %s>" % (self.name(), self.deviceId(), self.state())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _last_reading(self):
|
||||||
|
return self.jsonState.get('last_reading') or {}
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return self.jsonState.get('name', "Unknown Name")
|
||||||
|
|
||||||
|
def state(self):
|
||||||
|
return self._last_reading.get('opened', False)
|
||||||
|
|
||||||
|
def deviceId(self):
|
||||||
|
return self.jsonState.get('sensor_pod_id', self.name())
|
||||||
|
|
||||||
|
def refresh_state_at_hub(self):
|
||||||
|
"""
|
||||||
|
Tell hub to query latest status from device and upload to Wink.
|
||||||
|
PS: Not sure if this even works..
|
||||||
|
"""
|
||||||
|
urlString = baseUrl + "/%s/%s/refresh" % (self.objectprefix, self.deviceId())
|
||||||
|
requests.get(urlString, headers=headers)
|
||||||
|
|
||||||
|
def updateState(self):
|
||||||
|
""" Update state with latest info from Wink API. """
|
||||||
|
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
|
||||||
|
arequest = requests.get(urlString, headers=headers)
|
||||||
|
self._updateStateFromResponse(arequest.json())
|
||||||
|
|
||||||
|
def _updateStateFromResponse(self, response_json):
|
||||||
|
"""
|
||||||
|
:param response_json: the json obj returned from query
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.jsonState = response_json.get('data')
|
||||||
|
|
||||||
class wink_binary_switch(object):
|
class wink_binary_switch(object):
|
||||||
""" represents a wink.py switch
|
""" represents a wink.py switch
|
||||||
json_obj holds the json stat at init (and if there is a refresh it's updated
|
json_obj holds the json stat at init (and if there is a refresh it's updated
|
||||||
@ -248,53 +366,28 @@ class wink_bulb(wink_binary_switch):
|
|||||||
self.name(), self.deviceId(), self.state())
|
self.name(), self.deviceId(), self.state())
|
||||||
|
|
||||||
|
|
||||||
def get_bulbs_and_switches():
|
def get_devices(filter, constructor):
|
||||||
arequestUrl = baseUrl + "/users/me/wink_devices"
|
arequestUrl = baseUrl + "/users/me/wink_devices"
|
||||||
j = requests.get(arequestUrl, headers=headers).json()
|
j = requests.get(arequestUrl, headers=headers).json()
|
||||||
|
|
||||||
items = j.get('data')
|
items = j.get('data')
|
||||||
|
|
||||||
switches = []
|
devices = []
|
||||||
for item in items:
|
for item in items:
|
||||||
id = item.get('light_bulb_id')
|
id = item.get(filter)
|
||||||
if id != None:
|
if id is not None:
|
||||||
switches.append(wink_bulb(item))
|
devices.append(constructor(item))
|
||||||
id = item.get('binary_switch_id')
|
|
||||||
if id != None:
|
|
||||||
switches.append(wink_binary_switch(item))
|
|
||||||
|
|
||||||
return switches
|
|
||||||
|
|
||||||
|
return devices
|
||||||
|
|
||||||
def get_bulbs():
|
def get_bulbs():
|
||||||
arequestUrl = baseUrl + "/users/me/wink_devices"
|
return get_devices('light_bulb_id', wink_bulb)
|
||||||
j = requests.get(arequestUrl, headers=headers).json()
|
|
||||||
|
|
||||||
items = j.get('data')
|
|
||||||
|
|
||||||
switches = []
|
|
||||||
for item in items:
|
|
||||||
id = item.get('light_bulb_id')
|
|
||||||
if id is not None:
|
|
||||||
switches.append(wink_bulb(item))
|
|
||||||
|
|
||||||
return switches
|
|
||||||
|
|
||||||
|
|
||||||
def get_switches():
|
def get_switches():
|
||||||
arequestUrl = baseUrl + "/users/me/wink_devices"
|
return get_devices('binary_switch_id', wink_binary_switch)
|
||||||
j = requests.get(arequestUrl, headers=headers).json()
|
|
||||||
|
|
||||||
items = j.get('data')
|
|
||||||
|
|
||||||
switches = []
|
|
||||||
for item in items:
|
|
||||||
id = item.get('binary_switch_id')
|
|
||||||
if id is not None:
|
|
||||||
switches.append(wink_binary_switch(item))
|
|
||||||
|
|
||||||
return switches
|
|
||||||
|
|
||||||
|
def get_sensors():
|
||||||
|
return get_devices('sensor_pod_id', wink_sensor_pod)
|
||||||
|
|
||||||
def set_bearer_token(token):
|
def set_bearer_token(token):
|
||||||
global headers
|
global headers
|
||||||
|
Loading…
x
Reference in New Issue
Block a user