mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Homekit: Use util functions for unit conversion (#13253)
* Updated to util/color for conversion * Updated temperature sensor to use util/temperature conversion
This commit is contained in:
parent
99f7e2bd97
commit
f6ae2d338d
@ -5,6 +5,7 @@ from homeassistant.components.light import (
|
|||||||
ATTR_RGB_COLOR, ATTR_BRIGHTNESS,
|
ATTR_RGB_COLOR, ATTR_BRIGHTNESS,
|
||||||
SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR)
|
SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR)
|
||||||
from homeassistant.const import ATTR_SUPPORTED_FEATURES, STATE_ON, STATE_OFF
|
from homeassistant.const import ATTR_SUPPORTED_FEATURES, STATE_ON, STATE_OFF
|
||||||
|
from homeassistant.util.color import color_RGB_to_hsv, color_hsv_to_RGB
|
||||||
|
|
||||||
from . import TYPES
|
from . import TYPES
|
||||||
from .accessories import HomeAccessory, add_preload_service
|
from .accessories import HomeAccessory, add_preload_service
|
||||||
@ -17,69 +18,6 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
RGB_COLOR = 'rgb_color'
|
RGB_COLOR = 'rgb_color'
|
||||||
|
|
||||||
|
|
||||||
class Color:
|
|
||||||
"""Class to handle color conversions."""
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
|
|
||||||
def __init__(self, hue=None, saturation=None):
|
|
||||||
"""Initialize a new Color object."""
|
|
||||||
self.hue = hue # [0, 360]
|
|
||||||
self.saturation = saturation # [0, 1]
|
|
||||||
|
|
||||||
def calc_hsv_to_rgb(self):
|
|
||||||
"""Convert hsv_color value to rgb_color."""
|
|
||||||
if not self.hue or not self.saturation:
|
|
||||||
return [None] * 3
|
|
||||||
|
|
||||||
i = int(self.hue / 60)
|
|
||||||
f = self.hue / 60 - i
|
|
||||||
v = 1
|
|
||||||
p = 1 - self.saturation
|
|
||||||
q = 1 - self.saturation * f
|
|
||||||
t = 1 - self.saturation * (1 - f)
|
|
||||||
|
|
||||||
rgb = []
|
|
||||||
if i in [0, 6]:
|
|
||||||
rgb = [v, t, p]
|
|
||||||
elif i == 1:
|
|
||||||
rgb = [q, v, p]
|
|
||||||
elif i == 2:
|
|
||||||
rgb = [p, v, t]
|
|
||||||
elif i == 3:
|
|
||||||
rgb = [p, q, v]
|
|
||||||
elif i == 4:
|
|
||||||
rgb = [t, p, v]
|
|
||||||
elif i == 5:
|
|
||||||
rgb = [v, p, q]
|
|
||||||
|
|
||||||
return [round(c * 255) for c in rgb]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def calc_rgb_to_hsv(cls, rgb_color):
|
|
||||||
"""Convert a give rgb_color back to a hsv_color."""
|
|
||||||
rgb_color = [c / 255 for c in rgb_color]
|
|
||||||
c_max = max(rgb_color)
|
|
||||||
c_min = min(rgb_color)
|
|
||||||
c_diff = c_max - c_min
|
|
||||||
r, g, b = rgb_color
|
|
||||||
|
|
||||||
hue, saturation = 0, 0
|
|
||||||
if c_max == r:
|
|
||||||
hue = 60 * (0 + (g - b) / c_diff)
|
|
||||||
elif c_max == g:
|
|
||||||
hue = 60 * (2 + (b - r) / c_diff)
|
|
||||||
elif c_max == b:
|
|
||||||
hue = 60 * (4 + (r - g) / c_diff)
|
|
||||||
|
|
||||||
hue = round(hue + 360) if hue < 0 else round(hue)
|
|
||||||
|
|
||||||
if c_max != 0:
|
|
||||||
saturation = round((c_max - c_min) / c_max * 100)
|
|
||||||
|
|
||||||
return (hue, saturation)
|
|
||||||
|
|
||||||
|
|
||||||
@TYPES.register('Light')
|
@TYPES.register('Light')
|
||||||
class Light(HomeAccessory):
|
class Light(HomeAccessory):
|
||||||
"""Generate a Light accessory for a light entity.
|
"""Generate a Light accessory for a light entity.
|
||||||
@ -97,8 +35,6 @@ class Light(HomeAccessory):
|
|||||||
CHAR_HUE: False, CHAR_SATURATION: False,
|
CHAR_HUE: False, CHAR_SATURATION: False,
|
||||||
RGB_COLOR: False}
|
RGB_COLOR: False}
|
||||||
|
|
||||||
self.color = Color()
|
|
||||||
|
|
||||||
self.chars = []
|
self.chars = []
|
||||||
self._features = self._hass.states.get(self._entity_id) \
|
self._features = self._hass.states.get(self._entity_id) \
|
||||||
.attributes.get(ATTR_SUPPORTED_FEATURES)
|
.attributes.get(ATTR_SUPPORTED_FEATURES)
|
||||||
@ -107,6 +43,8 @@ class Light(HomeAccessory):
|
|||||||
if self._features & SUPPORT_RGB_COLOR:
|
if self._features & SUPPORT_RGB_COLOR:
|
||||||
self.chars.append(CHAR_HUE)
|
self.chars.append(CHAR_HUE)
|
||||||
self.chars.append(CHAR_SATURATION)
|
self.chars.append(CHAR_SATURATION)
|
||||||
|
self._hue = None
|
||||||
|
self._saturation = None
|
||||||
|
|
||||||
serv_light = add_preload_service(self, SERV_LIGHTBULB, self.chars)
|
serv_light = add_preload_service(self, SERV_LIGHTBULB, self.chars)
|
||||||
self.char_on = serv_light.get_characteristic(CHAR_ON)
|
self.char_on = serv_light.get_characteristic(CHAR_ON)
|
||||||
@ -152,14 +90,14 @@ class Light(HomeAccessory):
|
|||||||
"""Set saturation if call came from HomeKit."""
|
"""Set saturation if call came from HomeKit."""
|
||||||
_LOGGER.debug('%s: Set saturation to %d', self._entity_id, value)
|
_LOGGER.debug('%s: Set saturation to %d', self._entity_id, value)
|
||||||
self._flag[CHAR_SATURATION] = True
|
self._flag[CHAR_SATURATION] = True
|
||||||
self.color.saturation = value / 100
|
self._saturation = value
|
||||||
self.set_color()
|
self.set_color()
|
||||||
|
|
||||||
def set_hue(self, value):
|
def set_hue(self, value):
|
||||||
"""Set hue if call came from HomeKit."""
|
"""Set hue if call came from HomeKit."""
|
||||||
_LOGGER.debug('%s: Set hue to %d', self._entity_id, value)
|
_LOGGER.debug('%s: Set hue to %d', self._entity_id, value)
|
||||||
self._flag[CHAR_HUE] = True
|
self._flag[CHAR_HUE] = True
|
||||||
self.color.hue = value
|
self._hue = value
|
||||||
self.set_color()
|
self.set_color()
|
||||||
|
|
||||||
def set_color(self):
|
def set_color(self):
|
||||||
@ -167,7 +105,7 @@ class Light(HomeAccessory):
|
|||||||
# Handle RGB Color
|
# Handle RGB Color
|
||||||
if self._features & SUPPORT_RGB_COLOR and self._flag[CHAR_HUE] and \
|
if self._features & SUPPORT_RGB_COLOR and self._flag[CHAR_HUE] and \
|
||||||
self._flag[CHAR_SATURATION]:
|
self._flag[CHAR_SATURATION]:
|
||||||
color = self.color.calc_hsv_to_rgb()
|
color = color_hsv_to_RGB(self._hue, self._saturation, 100)
|
||||||
_LOGGER.debug('%s: Set rgb_color to %s', self._entity_id, color)
|
_LOGGER.debug('%s: Set rgb_color to %s', self._entity_id, color)
|
||||||
self._flag.update({
|
self._flag.update({
|
||||||
CHAR_HUE: False, CHAR_SATURATION: False, RGB_COLOR: True})
|
CHAR_HUE: False, CHAR_SATURATION: False, RGB_COLOR: True})
|
||||||
@ -199,10 +137,12 @@ class Light(HomeAccessory):
|
|||||||
# Handle RGB Color
|
# Handle RGB Color
|
||||||
if CHAR_SATURATION in self.chars and CHAR_HUE in self.chars:
|
if CHAR_SATURATION in self.chars and CHAR_HUE in self.chars:
|
||||||
rgb_color = new_state.attributes.get(ATTR_RGB_COLOR)
|
rgb_color = new_state.attributes.get(ATTR_RGB_COLOR)
|
||||||
|
current_color = color_hsv_to_RGB(self._hue, self._saturation, 100)\
|
||||||
|
if self._hue and self._saturation else [None] * 3
|
||||||
if not self._flag[RGB_COLOR] and \
|
if not self._flag[RGB_COLOR] and \
|
||||||
isinstance(rgb_color, (list, tuple)) and \
|
isinstance(rgb_color, (list, tuple)) and \
|
||||||
list(rgb_color) != self.color.calc_hsv_to_rgb():
|
tuple(rgb_color) != current_color:
|
||||||
hue, saturation = Color.calc_rgb_to_hsv(rgb_color)
|
hue, saturation, _ = color_RGB_to_hsv(*rgb_color)
|
||||||
self.char_hue.set_value(hue, should_callback=False)
|
self.char_hue.set_value(hue, should_callback=False)
|
||||||
self.char_saturation.set_value(saturation,
|
self.char_saturation.set_value(saturation,
|
||||||
should_callback=False)
|
should_callback=False)
|
||||||
|
@ -3,6 +3,7 @@ import logging
|
|||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_UNIT_OF_MEASUREMENT, TEMP_FAHRENHEIT, TEMP_CELSIUS)
|
ATTR_UNIT_OF_MEASUREMENT, TEMP_FAHRENHEIT, TEMP_CELSIUS)
|
||||||
|
from homeassistant.util.temperature import fahrenheit_to_celsius
|
||||||
|
|
||||||
from . import TYPES
|
from . import TYPES
|
||||||
from .accessories import (
|
from .accessories import (
|
||||||
@ -26,7 +27,7 @@ def calc_temperature(state, unit=TEMP_CELSIUS):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return round((value - 32) / 1.8, 2) if unit == TEMP_FAHRENHEIT else value
|
return fahrenheit_to_celsius(value) if unit == TEMP_FAHRENHEIT else value
|
||||||
|
|
||||||
|
|
||||||
def calc_humidity(state):
|
def calc_humidity(state):
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.components.homekit.type_lights import Light, Color
|
from homeassistant.components.homekit.type_lights import Light
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
DOMAIN, ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_RGB_COLOR,
|
DOMAIN, ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, ATTR_RGB_COLOR,
|
||||||
SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR)
|
SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR)
|
||||||
@ -14,34 +14,6 @@ from homeassistant.const import (
|
|||||||
from tests.common import get_test_home_assistant
|
from tests.common import get_test_home_assistant
|
||||||
|
|
||||||
|
|
||||||
def test_calc_hsv_to_rgb():
|
|
||||||
"""Test conversion hsv to rgb."""
|
|
||||||
color = Color(43, 23 / 100)
|
|
||||||
assert color.calc_hsv_to_rgb() == [255, 238, 196]
|
|
||||||
|
|
||||||
color.hue, color.saturation = (79, 12 / 100)
|
|
||||||
assert color.calc_hsv_to_rgb() == [245, 255, 224]
|
|
||||||
|
|
||||||
color.hue, color.saturation = (177, 2 / 100)
|
|
||||||
assert color.calc_hsv_to_rgb() == [250, 255, 255]
|
|
||||||
|
|
||||||
color.hue, color.saturation = (212, 26 / 100)
|
|
||||||
assert color.calc_hsv_to_rgb() == [189, 220, 255]
|
|
||||||
|
|
||||||
color.hue, color.saturation = (271, 93 / 100)
|
|
||||||
assert color.calc_hsv_to_rgb() == [140, 18, 255]
|
|
||||||
|
|
||||||
color.hue, color.saturation = (355, 100 / 100)
|
|
||||||
assert color.calc_hsv_to_rgb() == [255, 0, 21]
|
|
||||||
|
|
||||||
|
|
||||||
def test_calc_rgb_to_hsv():
|
|
||||||
"""Test conversion rgb to hsv."""
|
|
||||||
assert Color.calc_rgb_to_hsv([255, 0, 21]) == (355, 100)
|
|
||||||
assert Color.calc_rgb_to_hsv([245, 255, 224]) == (79, 12)
|
|
||||||
assert Color.calc_rgb_to_hsv([189, 220, 255]) == (212, 26)
|
|
||||||
|
|
||||||
|
|
||||||
class TestHomekitLights(unittest.TestCase):
|
class TestHomekitLights(unittest.TestCase):
|
||||||
"""Test class for all accessory types regarding lights."""
|
"""Test class for all accessory types regarding lights."""
|
||||||
|
|
||||||
@ -137,15 +109,15 @@ class TestHomekitLights(unittest.TestCase):
|
|||||||
entity_id = 'light.demo'
|
entity_id = 'light.demo'
|
||||||
self.hass.states.set(entity_id, STATE_ON, {
|
self.hass.states.set(entity_id, STATE_ON, {
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_RGB_COLOR,
|
ATTR_SUPPORTED_FEATURES: SUPPORT_RGB_COLOR,
|
||||||
ATTR_RGB_COLOR: (120, 20, 300)})
|
ATTR_RGB_COLOR: (120, 20, 255)})
|
||||||
acc = Light(self.hass, entity_id, 'Light', aid=2)
|
acc = Light(self.hass, entity_id, 'Light', aid=2)
|
||||||
self.assertEqual(acc.char_hue.value, 0)
|
self.assertEqual(acc.char_hue.value, 0)
|
||||||
self.assertEqual(acc.char_saturation.value, 75)
|
self.assertEqual(acc.char_saturation.value, 75)
|
||||||
|
|
||||||
acc.run()
|
acc.run()
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
self.assertEqual(acc.char_hue.value, 261)
|
self.assertEqual(acc.char_hue.value, 265.532)
|
||||||
self.assertEqual(acc.char_saturation.value, 93)
|
self.assertEqual(acc.char_saturation.value, 92.157)
|
||||||
|
|
||||||
# Set from HomeKit
|
# Set from HomeKit
|
||||||
acc.char_hue.set_value(145)
|
acc.char_hue.set_value(145)
|
||||||
@ -157,4 +129,4 @@ class TestHomekitLights(unittest.TestCase):
|
|||||||
self.events[0].data[ATTR_SERVICE], SERVICE_TURN_ON)
|
self.events[0].data[ATTR_SERVICE], SERVICE_TURN_ON)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.events[0].data[ATTR_SERVICE_DATA], {
|
self.events[0].data[ATTR_SERVICE_DATA], {
|
||||||
ATTR_ENTITY_ID: entity_id, ATTR_RGB_COLOR: [64, 255, 143]})
|
ATTR_ENTITY_ID: entity_id, ATTR_RGB_COLOR: (63, 255, 143)})
|
||||||
|
@ -17,9 +17,7 @@ def test_calc_temperature():
|
|||||||
|
|
||||||
assert calc_temperature('20') == 20
|
assert calc_temperature('20') == 20
|
||||||
assert calc_temperature('20.12', TEMP_CELSIUS) == 20.12
|
assert calc_temperature('20.12', TEMP_CELSIUS) == 20.12
|
||||||
|
|
||||||
assert calc_temperature('75.2', TEMP_FAHRENHEIT) == 24
|
assert calc_temperature('75.2', TEMP_FAHRENHEIT) == 24
|
||||||
assert calc_temperature('-20.6', TEMP_FAHRENHEIT) == -29.22
|
|
||||||
|
|
||||||
|
|
||||||
def test_calc_humidity():
|
def test_calc_humidity():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user