Bug fixes for Wink

This commit is contained in:
Paulus Schoutsen 2015-01-15 21:25:24 -08:00
parent c116cb095d
commit 702498ca09
7 changed files with 193 additions and 140 deletions

View File

@ -178,7 +178,7 @@ def setup(hass, config):
_LOGGER.info("Updating light states")
for light in lights.values():
light.update_ha_state(hass)
light.update_ha_state(hass, True)
update_lights_state(None)
@ -196,7 +196,7 @@ def setup(hass, config):
for light in discovered:
if light is not None and light not in lights.values():
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[light.entity_id] = light

View File

@ -4,8 +4,9 @@ import logging
# pylint: disable=no-name-in-module, import-error
import homeassistant.external.wink.pywink as pywink
from homeassistant.helpers import ToggleDevice
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_ACCESS_TOKEN
from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.components.wink import WinkToggleDevice
from homeassistant.const import CONF_ACCESS_TOKEN
# pylint: disable=unused-argument
@ -35,29 +36,28 @@ def get_lights():
return [WinkLight(light) for light in pywink.get_bulbs()]
class WinkLight(ToggleDevice):
class WinkLight(WinkToggleDevice):
""" Represents a Wink light """
def __init__(self, wink):
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()
# pylint: disable=too-few-public-methods
def turn_on(self, **kwargs):
""" Turns the light on. """
self.wink.setState(True)
""" Turns the switch on. """
brightness = kwargs.get(ATTR_BRIGHTNESS)
def turn_off(self):
""" Turns the light off. """
self.wink.setState(False)
if brightness is not None:
self.wink.setState(True, brightness / 255)
def is_on(self):
""" True if light is on. """
return self.wink.state()
else:
self.wink.setState(True)
def get_state_attributes(self):
""" Returns optional state attributes. """
return self.state_attr
@property
def state_attributes(self):
attr = super().state_attributes
if self.is_on:
brightness = self.wink.brightness()
if brightness is not None:
attr[ATTR_BRIGHTNESS] = int(brightness * 255)
return attr

View File

@ -73,7 +73,7 @@ def setup(hass, config):
logger.info("Updating switch states")
for switch in switches.values():
switch.update_ha_state(hass)
switch.update_ha_state(hass, True)
update_states(None)

View File

@ -86,7 +86,7 @@ class WemoSwitch(ToggleDevice):
@property
def is_on(self):
""" True if switch is on. """
return self.wemo.get_state(True)
return self.wemo.get_state()
def turn_on(self, **kwargs):
""" Turns the switch on. """
@ -95,3 +95,7 @@ class WemoSwitch(ToggleDevice):
def turn_off(self):
""" Turns the switch off. """
self.wemo.off()
def update(self):
""" Update Wemo state. """
self.wemo.get_state(True)

View File

@ -4,8 +4,8 @@ import logging
# pylint: disable=no-name-in-module, import-error
import homeassistant.external.wink.pywink as pywink
from homeassistant.helpers import ToggleDevice
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
@ -32,32 +32,4 @@ def devices_discovered(hass, config, info):
def get_switches():
""" Returns the Wink switches. """
return [WinkSwitch(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
return [WinkToggleDevice(switch) for switch in pywink.get_switches()]

View File

@ -8,10 +8,10 @@ import homeassistant.external.wink.pywink as pywink
from homeassistant import bootstrap
from homeassistant.loader import get_component
from homeassistant.helpers import validate_config
from homeassistant.helpers import validate_config, ToggleDevice
from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED,
CONF_ACCESS_TOKEN)
EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN,
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
DOMAIN = "wink"
DEPENDENCIES = []
@ -48,3 +48,44 @@ def setup(hass, config):
})
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()

View File

@ -1,21 +1,16 @@
__author__ = 'JOHNMCL'
import json
import time
import requests
baseUrl = "https://winkapi.quirky.com"
object_type = "light_bulb"
object_type_plural = "light_bulbs"
bearer_token=""
headers = {}
class wink_binary_switch():
class wink_binary_switch(object):
""" represents a wink.py switch
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
@ -85,14 +80,11 @@ class wink_binary_switch():
}
"""
jsonState = {}
def __init__(self, aJSonObj):
def __init__(self, aJSonObj, objectprefix="binary_switches"):
self.jsonState = aJSonObj
self.objectprefix = "binary_switches"
self.objectprefix = objectprefix
# Tuple (desired state, time)
self._last_call = (0, None)
def __str__(self):
return "%s %s %s" % (self.name(), self.deviceId(), self.state())
@ -100,13 +92,23 @@ class wink_binary_switch():
def __repr__(self):
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):
name = self.jsonState.get('name')
return name or "Unknown Name"
return self.jsonState.get('name', "Unknown Name")
def state(self):
state = self.jsonState.get('desired_state').get('powered')
return state
# Optimistic approach to setState:
# 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):
"""
@ -115,20 +117,47 @@ class wink_binary_switch():
"""
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
values = {"desired_state": {"powered": state}}
urlString = baseUrl + "/%s/%s" % (self.objectprefix, self.deviceId())
arequest = requests.put(urlString, data=json.dumps(values), headers=headers)
self._updateStateFromResponse(arequest.json())
self._last_call = (time.time(), state)
def deviceId(self):
deviceId = self.jsonState.get('binary_switch_id')
return deviceId or "Unknown Device ID"
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 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):
"""
:param response_json: the json obj returned from query
@ -136,6 +165,10 @@ class wink_binary_switch():
"""
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):
""" represents a wink.py bulb
json_obj holds the json stat at init (and if there is a refresh it's updated
@ -143,77 +176,76 @@ class wink_bulb(wink_binary_switch):
and looks like so:
"light_bulb_id": "33990",
"name": "downstaurs lamp",
"locale": "en_us",
"units":{},
"created_at": 1410925804,
"hidden_at": null,
"capabilities":{},
"subscription":{},
"triggers":[],
"desired_state":{"powered": true, "brightness": 1},
"manufacturer_device_model": "lutron_p_pkg1_w_wh_d",
"manufacturer_device_id": null,
"device_manufacturer": "lutron",
"model_name": "Caseta Wireless Dimmer & Pico",
"upc_id": "3",
"hub_id": "11780",
"local_id": "8",
"radio_type": "lutron",
"linked_service_id": null,
"last_reading":{
"brightness": 1,
"brightness_updated_at": 1417823487.490747,
"connection": true,
"connection_updated_at": 1417823487.4907365,
"powered": true,
"powered_updated_at": 1417823487.4907532,
"desired_powered": true,
"desired_powered_updated_at": 1417823485.054675,
"desired_brightness": 1,
"desired_brightness_updated_at": 1417409293.2591703
},
"lat_lng":[38.429962, -122.653715],
"location": "",
"order": 0
"name": "downstaurs lamp",
"locale": "en_us",
"units":{},
"created_at": 1410925804,
"hidden_at": null,
"capabilities":{},
"subscription":{},
"triggers":[],
"desired_state":{"powered": true, "brightness": 1},
"manufacturer_device_model": "lutron_p_pkg1_w_wh_d",
"manufacturer_device_id": null,
"device_manufacturer": "lutron",
"model_name": "Caseta Wireless Dimmer & Pico",
"upc_id": "3",
"hub_id": "11780",
"local_id": "8",
"radio_type": "lutron",
"linked_service_id": null,
"last_reading":{
"brightness": 1,
"brightness_updated_at": 1417823487.490747,
"connection": true,
"connection_updated_at": 1417823487.4907365,
"powered": true,
"powered_updated_at": 1417823487.4907532,
"desired_powered": true,
"desired_powered_updated_at": 1417823485.054675,
"desired_brightness": 1,
"desired_brightness_updated_at": 1417409293.2591703
},
"lat_lng":[38.429962, -122.653715],
"location": "",
"order": 0
"""
jsonState = {}
def __init__(self, ajsonobj):
self.jsonState = ajsonobj
self.objectprefix = "light_bulbs"
super().__init__(ajsonobj, "light_bulbs")
def __str__(self):
return "%s %s %s" % (self.name(), self.deviceId(), self.state())
def deviceId(self):
return self.jsonState.get('light_bulb_id', self.name())
def __repr__(self):
return "<Wink Bulb %s %s %s>" % (self.name(), self.deviceId(), self.state())
def brightness(self):
return self._last_reading.get('brightness')
def name(self):
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):
def setState(self, state, brightness=None):
"""
:param state: a boolean of true (on) or false ('off')
:return: nothing
"""
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()
arequest = requests.put(urlString, data=json.dumps(values), headers=headers)
self._updateStateFromResponse(arequest.json())
self.updateState()
self._last_call = (time.time(), state)
def deviceId(self):
deviceId = self.jsonState.get('light_bulb_id')
return deviceId or "Unknown Device ID"
def __repr__(self):
return "<Wink Bulb %s %s %s>" % (
self.name(), self.deviceId(), self.state())
def get_bulbs_and_switches():
@ -243,7 +275,7 @@ def get_bulbs():
switches = []
for item in items:
id = item.get('light_bulb_id')
if id != None:
if id is not None:
switches.append(wink_bulb(item))
return switches
@ -258,15 +290,19 @@ def get_switches():
switches = []
for item in items:
id = item.get('binary_switch_id')
if id != None:
if id is not None:
switches.append(wink_binary_switch(item))
return switches
def set_bearer_token(token):
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__":
sw = get_bulbs()