mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Bug fixes for Wink
This commit is contained in:
parent
c116cb095d
commit
702498ca09
@ -178,7 +178,7 @@ def setup(hass, config):
|
|||||||
_LOGGER.info("Updating light states")
|
_LOGGER.info("Updating light states")
|
||||||
|
|
||||||
for light in lights.values():
|
for light in lights.values():
|
||||||
light.update_ha_state(hass)
|
light.update_ha_state(hass, True)
|
||||||
|
|
||||||
update_lights_state(None)
|
update_lights_state(None)
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ def setup(hass, config):
|
|||||||
for light in discovered:
|
for light in discovered:
|
||||||
if light is not None and light not in lights.values():
|
if light is not None and light not in lights.values():
|
||||||
light.entity_id = util.ensure_unique_string(
|
light.entity_id = util.ensure_unique_string(
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(light.get_name())),
|
ENTITY_ID_FORMAT.format(util.slugify(light.name)),
|
||||||
lights.keys())
|
lights.keys())
|
||||||
|
|
||||||
lights[light.entity_id] = light
|
lights[light.entity_id] = light
|
||||||
|
@ -4,8 +4,9 @@ import logging
|
|||||||
# pylint: disable=no-name-in-module, import-error
|
# pylint: disable=no-name-in-module, import-error
|
||||||
import homeassistant.external.wink.pywink as pywink
|
import homeassistant.external.wink.pywink as pywink
|
||||||
|
|
||||||
from homeassistant.helpers import ToggleDevice
|
from homeassistant.components.light import ATTR_BRIGHTNESS
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_ACCESS_TOKEN
|
from homeassistant.components.wink import WinkToggleDevice
|
||||||
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
@ -35,29 +36,28 @@ def get_lights():
|
|||||||
return [WinkLight(light) for light in pywink.get_bulbs()]
|
return [WinkLight(light) for light in pywink.get_bulbs()]
|
||||||
|
|
||||||
|
|
||||||
class WinkLight(ToggleDevice):
|
class WinkLight(WinkToggleDevice):
|
||||||
""" Represents a Wink light """
|
""" Represents a Wink light """
|
||||||
|
|
||||||
def __init__(self, wink):
|
# pylint: disable=too-few-public-methods
|
||||||
self.wink = wink
|
|
||||||
self.state_attr = {ATTR_FRIENDLY_NAME: wink.name()}
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
""" Returns the name of the light if any. """
|
|
||||||
return self.wink.name()
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
def turn_on(self, **kwargs):
|
||||||
""" Turns the light on. """
|
""" Turns the switch on. """
|
||||||
|
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||||
|
|
||||||
|
if brightness is not None:
|
||||||
|
self.wink.setState(True, brightness / 255)
|
||||||
|
|
||||||
|
else:
|
||||||
self.wink.setState(True)
|
self.wink.setState(True)
|
||||||
|
|
||||||
def turn_off(self):
|
@property
|
||||||
""" Turns the light off. """
|
def state_attributes(self):
|
||||||
self.wink.setState(False)
|
attr = super().state_attributes
|
||||||
|
|
||||||
def is_on(self):
|
if self.is_on:
|
||||||
""" True if light is on. """
|
brightness = self.wink.brightness()
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
def get_state_attributes(self):
|
if brightness is not None:
|
||||||
""" Returns optional state attributes. """
|
attr[ATTR_BRIGHTNESS] = int(brightness * 255)
|
||||||
return self.state_attr
|
|
||||||
|
return attr
|
||||||
|
@ -73,7 +73,7 @@ def setup(hass, config):
|
|||||||
logger.info("Updating switch states")
|
logger.info("Updating switch states")
|
||||||
|
|
||||||
for switch in switches.values():
|
for switch in switches.values():
|
||||||
switch.update_ha_state(hass)
|
switch.update_ha_state(hass, True)
|
||||||
|
|
||||||
update_states(None)
|
update_states(None)
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class WemoSwitch(ToggleDevice):
|
|||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" True if switch is on. """
|
""" True if switch is on. """
|
||||||
return self.wemo.get_state(True)
|
return self.wemo.get_state()
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
def turn_on(self, **kwargs):
|
||||||
""" Turns the switch on. """
|
""" Turns the switch on. """
|
||||||
@ -95,3 +95,7 @@ class WemoSwitch(ToggleDevice):
|
|||||||
def turn_off(self):
|
def turn_off(self):
|
||||||
""" Turns the switch off. """
|
""" Turns the switch off. """
|
||||||
self.wemo.off()
|
self.wemo.off()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
""" Update Wemo state. """
|
||||||
|
self.wemo.get_state(True)
|
||||||
|
@ -4,8 +4,8 @@ import logging
|
|||||||
# pylint: disable=no-name-in-module, import-error
|
# pylint: disable=no-name-in-module, import-error
|
||||||
import homeassistant.external.wink.pywink as pywink
|
import homeassistant.external.wink.pywink as pywink
|
||||||
|
|
||||||
from homeassistant.helpers import ToggleDevice
|
from homeassistant.components.wink import WinkToggleDevice
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
@ -32,32 +32,4 @@ def devices_discovered(hass, config, info):
|
|||||||
|
|
||||||
def get_switches():
|
def get_switches():
|
||||||
""" Returns the Wink switches. """
|
""" Returns the Wink switches. """
|
||||||
return [WinkSwitch(switch) for switch in pywink.get_switches()]
|
return [WinkToggleDevice(switch) for switch in pywink.get_switches()]
|
||||||
|
|
||||||
|
|
||||||
class WinkSwitch(ToggleDevice):
|
|
||||||
""" represents a WeMo switch within home assistant. """
|
|
||||||
|
|
||||||
def __init__(self, wink):
|
|
||||||
self.wink = wink
|
|
||||||
self.state_attr = {ATTR_FRIENDLY_NAME: wink.name()}
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
""" Returns the name of the switch if any. """
|
|
||||||
return self.wink.name()
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
|
||||||
""" Turns the switch on. """
|
|
||||||
self.wink.setState(True)
|
|
||||||
|
|
||||||
def turn_off(self):
|
|
||||||
""" Turns the switch off. """
|
|
||||||
self.wink.setState(False)
|
|
||||||
|
|
||||||
def is_on(self):
|
|
||||||
""" True if switch is on. """
|
|
||||||
return self.wink.state()
|
|
||||||
|
|
||||||
def get_state_attributes(self):
|
|
||||||
""" Returns optional state attributes. """
|
|
||||||
return self.state_attr
|
|
||||||
|
@ -8,10 +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
|
from homeassistant.helpers import validate_config, ToggleDevice
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED,
|
EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN,
|
||||||
CONF_ACCESS_TOKEN)
|
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
|
||||||
|
|
||||||
DOMAIN = "wink"
|
DOMAIN = "wink"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
@ -48,3 +48,44 @@ def setup(hass, config):
|
|||||||
})
|
})
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class WinkToggleDevice(ToggleDevice):
|
||||||
|
""" represents a WeMo switch within home assistant. """
|
||||||
|
|
||||||
|
def __init__(self, wink):
|
||||||
|
self.wink = wink
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
""" Returns the id of this WeMo switch """
|
||||||
|
return "{}.{}".format(self.__class__, self.wink.deviceId())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
""" Returns the name of the light if any. """
|
||||||
|
return self.wink.name()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
""" True if light is on. """
|
||||||
|
return self.wink.state()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
""" Returns optional state attributes. """
|
||||||
|
return {
|
||||||
|
ATTR_FRIENDLY_NAME: self.wink.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
""" Turns the switch on. """
|
||||||
|
self.wink.setState(True)
|
||||||
|
|
||||||
|
def turn_off(self):
|
||||||
|
""" Turns the switch off. """
|
||||||
|
self.wink.setState(False)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
""" Update state of the light. """
|
||||||
|
self.wink.wait_till_desired_reached()
|
||||||
|
130
homeassistant/external/wink/pywink.py
vendored
130
homeassistant/external/wink/pywink.py
vendored
@ -1,21 +1,16 @@
|
|||||||
__author__ = 'JOHNMCL'
|
__author__ = 'JOHNMCL'
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
baseUrl = "https://winkapi.quirky.com"
|
baseUrl = "https://winkapi.quirky.com"
|
||||||
|
|
||||||
object_type = "light_bulb"
|
|
||||||
object_type_plural = "light_bulbs"
|
|
||||||
|
|
||||||
bearer_token=""
|
|
||||||
|
|
||||||
headers = {}
|
headers = {}
|
||||||
|
|
||||||
|
|
||||||
class wink_binary_switch():
|
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
|
||||||
it's the native format for this objects methods
|
it's the native format for this objects methods
|
||||||
@ -85,14 +80,11 @@ class wink_binary_switch():
|
|||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
jsonState = {}
|
def __init__(self, aJSonObj, objectprefix="binary_switches"):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, aJSonObj):
|
|
||||||
self.jsonState = aJSonObj
|
self.jsonState = aJSonObj
|
||||||
self.objectprefix = "binary_switches"
|
self.objectprefix = objectprefix
|
||||||
|
# Tuple (desired state, time)
|
||||||
|
self._last_call = (0, None)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s %s %s" % (self.name(), self.deviceId(), self.state())
|
return "%s %s %s" % (self.name(), self.deviceId(), self.state())
|
||||||
@ -100,13 +92,23 @@ class wink_binary_switch():
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Wink switch %s %s %s>" % (self.name(), self.deviceId(), self.state())
|
return "<Wink switch %s %s %s>" % (self.name(), self.deviceId(), self.state())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _last_reading(self):
|
||||||
|
return self.jsonState.get('last_reading') or {}
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
name = self.jsonState.get('name')
|
return self.jsonState.get('name', "Unknown Name")
|
||||||
return name or "Unknown Name"
|
|
||||||
|
|
||||||
def state(self):
|
def state(self):
|
||||||
state = self.jsonState.get('desired_state').get('powered')
|
# Optimistic approach to setState:
|
||||||
return state
|
# Within 15 seconds of a call to setState we assume it worked.
|
||||||
|
if self._recent_state_set():
|
||||||
|
return self._last_call[1]
|
||||||
|
|
||||||
|
return self._last_reading.get('powered', False)
|
||||||
|
|
||||||
|
def deviceId(self):
|
||||||
|
return self.jsonState.get('binary_switch_id', self.name())
|
||||||
|
|
||||||
def setState(self, state):
|
def setState(self, state):
|
||||||
"""
|
"""
|
||||||
@ -115,20 +117,47 @@ class wink_binary_switch():
|
|||||||
"""
|
"""
|
||||||
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
|
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
|
||||||
values = {"desired_state": {"powered": state}}
|
values = {"desired_state": {"powered": state}}
|
||||||
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
|
|
||||||
arequest = requests.put(urlString, data=json.dumps(values), headers=headers)
|
arequest = requests.put(urlString, data=json.dumps(values), headers=headers)
|
||||||
self._updateStateFromResponse(arequest.json())
|
self._updateStateFromResponse(arequest.json())
|
||||||
|
|
||||||
|
self._last_call = (time.time(), state)
|
||||||
|
|
||||||
def deviceId(self):
|
def refresh_state_at_hub(self):
|
||||||
deviceId = self.jsonState.get('binary_switch_id')
|
"""
|
||||||
return deviceId or "Unknown Device ID"
|
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):
|
def updateState(self):
|
||||||
|
""" Update state with latest info from Wink API. """
|
||||||
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
|
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
|
||||||
arequest = requests.get(urlString, headers=headers)
|
arequest = requests.get(urlString, headers=headers)
|
||||||
self._updateStateFromResponse(arequest.json())
|
self._updateStateFromResponse(arequest.json())
|
||||||
|
|
||||||
|
def wait_till_desired_reached(self):
|
||||||
|
""" Wait till desired state reached. Max 10s. """
|
||||||
|
if self._recent_state_set():
|
||||||
|
return
|
||||||
|
|
||||||
|
# self.refresh_state_at_hub()
|
||||||
|
tries = 1
|
||||||
|
|
||||||
|
while True:
|
||||||
|
self.updateState()
|
||||||
|
last_read = self._last_reading
|
||||||
|
|
||||||
|
if last_read.get('desired_powered') == last_read.get('powered') \
|
||||||
|
or tries == 5:
|
||||||
|
break
|
||||||
|
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
tries += 1
|
||||||
|
self.updateState()
|
||||||
|
last_read = self._last_reading
|
||||||
|
|
||||||
def _updateStateFromResponse(self, response_json):
|
def _updateStateFromResponse(self, response_json):
|
||||||
"""
|
"""
|
||||||
:param response_json: the json obj returned from query
|
:param response_json: the json obj returned from query
|
||||||
@ -136,6 +165,10 @@ class wink_binary_switch():
|
|||||||
"""
|
"""
|
||||||
self.jsonState = response_json.get('data')
|
self.jsonState = response_json.get('data')
|
||||||
|
|
||||||
|
def _recent_state_set(self):
|
||||||
|
return time.time() - self._last_call[0] < 15
|
||||||
|
|
||||||
|
|
||||||
class wink_bulb(wink_binary_switch):
|
class wink_bulb(wink_binary_switch):
|
||||||
""" represents a wink.py bulb
|
""" represents a wink.py bulb
|
||||||
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
|
||||||
@ -181,39 +214,38 @@ class wink_bulb(wink_binary_switch):
|
|||||||
jsonState = {}
|
jsonState = {}
|
||||||
|
|
||||||
def __init__(self, ajsonobj):
|
def __init__(self, ajsonobj):
|
||||||
self.jsonState = ajsonobj
|
super().__init__(ajsonobj, "light_bulbs")
|
||||||
self.objectprefix = "light_bulbs"
|
|
||||||
|
|
||||||
def __str__(self):
|
def deviceId(self):
|
||||||
return "%s %s %s" % (self.name(), self.deviceId(), self.state())
|
return self.jsonState.get('light_bulb_id', self.name())
|
||||||
|
|
||||||
def __repr__(self):
|
def brightness(self):
|
||||||
return "<Wink Bulb %s %s %s>" % (self.name(), self.deviceId(), self.state())
|
return self._last_reading.get('brightness')
|
||||||
|
|
||||||
def name(self):
|
def setState(self, state, brightness=None):
|
||||||
name = self.jsonState.get('name')
|
|
||||||
return name or "Unknown Name"
|
|
||||||
|
|
||||||
def state(self):
|
|
||||||
state = self.jsonState.get('desired_state').get('powered')
|
|
||||||
return state
|
|
||||||
|
|
||||||
def setState(self, state):
|
|
||||||
"""
|
"""
|
||||||
:param state: a boolean of true (on) or false ('off')
|
:param state: a boolean of true (on) or false ('off')
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
urlString = baseUrl + "/light_bulbs/%s" % self.deviceId()
|
urlString = baseUrl + "/light_bulbs/%s" % self.deviceId()
|
||||||
values = {"desired_state": {"desired_powered": state, "powered": state}}
|
values = {
|
||||||
|
"desired_state": {
|
||||||
|
"powered": state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if brightness is not None:
|
||||||
|
values["desired_state"]["brightness"] = brightness
|
||||||
|
|
||||||
urlString = baseUrl + "/light_bulbs/%s" % self.deviceId()
|
urlString = baseUrl + "/light_bulbs/%s" % self.deviceId()
|
||||||
arequest = requests.put(urlString, data=json.dumps(values), headers=headers)
|
arequest = requests.put(urlString, data=json.dumps(values), headers=headers)
|
||||||
|
self._updateStateFromResponse(arequest.json())
|
||||||
|
|
||||||
self.updateState()
|
self._last_call = (time.time(), state)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
def deviceId(self):
|
return "<Wink Bulb %s %s %s>" % (
|
||||||
deviceId = self.jsonState.get('light_bulb_id')
|
self.name(), self.deviceId(), self.state())
|
||||||
return deviceId or "Unknown Device ID"
|
|
||||||
|
|
||||||
|
|
||||||
def get_bulbs_and_switches():
|
def get_bulbs_and_switches():
|
||||||
@ -243,7 +275,7 @@ def get_bulbs():
|
|||||||
switches = []
|
switches = []
|
||||||
for item in items:
|
for item in items:
|
||||||
id = item.get('light_bulb_id')
|
id = item.get('light_bulb_id')
|
||||||
if id != None:
|
if id is not None:
|
||||||
switches.append(wink_bulb(item))
|
switches.append(wink_bulb(item))
|
||||||
|
|
||||||
return switches
|
return switches
|
||||||
@ -258,15 +290,19 @@ def get_switches():
|
|||||||
switches = []
|
switches = []
|
||||||
for item in items:
|
for item in items:
|
||||||
id = item.get('binary_switch_id')
|
id = item.get('binary_switch_id')
|
||||||
if id != None:
|
if id is not None:
|
||||||
switches.append(wink_binary_switch(item))
|
switches.append(wink_binary_switch(item))
|
||||||
|
|
||||||
return switches
|
return switches
|
||||||
|
|
||||||
|
|
||||||
def set_bearer_token(token):
|
def set_bearer_token(token):
|
||||||
global headers
|
global headers
|
||||||
bearer_token=token
|
|
||||||
headers={"Content-Type": "application/json", "Authorization": "Bearer {}".format(token)}
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": "Bearer {}".format(token)
|
||||||
|
}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sw = get_bulbs()
|
sw = get_bulbs()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user