mirror of
https://github.com/home-assistant/core.git
synced 2025-07-11 15:27:08 +00:00
flux: fix for when stop_time is after midnight (#8932)
* flux: fix for when stop_time is after midnight * flux: fix imports * flux: add missing check when now is after midnight * flux: one more try; should fix all use cases now * flux switch: fix lint * flux switch: add new tests * flux switch: fix tests lint * flux switch: fix tests docstrings
This commit is contained in:
parent
f51163f803
commit
0889e38cb1
@ -6,8 +6,9 @@ The idea was taken from https://github.com/KpaBap/hue-flux/
|
|||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/switch.flux/
|
https://home-assistant.io/components/switch.flux/
|
||||||
"""
|
"""
|
||||||
from datetime import time
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.light import is_on, turn_on
|
from homeassistant.components.light import is_on, turn_on
|
||||||
@ -46,7 +47,7 @@ PLATFORM_SCHEMA = vol.Schema({
|
|||||||
vol.Required(CONF_LIGHTS): cv.entity_ids,
|
vol.Required(CONF_LIGHTS): cv.entity_ids,
|
||||||
vol.Optional(CONF_NAME, default="Flux"): cv.string,
|
vol.Optional(CONF_NAME, default="Flux"): cv.string,
|
||||||
vol.Optional(CONF_START_TIME): cv.time,
|
vol.Optional(CONF_START_TIME): cv.time,
|
||||||
vol.Optional(CONF_STOP_TIME, default=time(22, 0)): cv.time,
|
vol.Optional(CONF_STOP_TIME, default=datetime.time(22, 0)): cv.time,
|
||||||
vol.Optional(CONF_START_CT, default=4000):
|
vol.Optional(CONF_START_CT, default=4000):
|
||||||
vol.All(vol.Coerce(int), vol.Range(min=1000, max=40000)),
|
vol.All(vol.Coerce(int), vol.Range(min=1000, max=40000)),
|
||||||
vol.Optional(CONF_SUNSET_CT, default=3000):
|
vol.Optional(CONF_SUNSET_CT, default=3000):
|
||||||
@ -171,12 +172,22 @@ class FluxSwitch(SwitchDevice):
|
|||||||
"""Update all the lights using flux."""
|
"""Update all the lights using flux."""
|
||||||
if now is None:
|
if now is None:
|
||||||
now = dt_now()
|
now = dt_now()
|
||||||
|
|
||||||
sunset = get_astral_event_date(self.hass, 'sunset', now.date())
|
sunset = get_astral_event_date(self.hass, 'sunset', now.date())
|
||||||
start_time = self.find_start_time(now)
|
start_time = self.find_start_time(now)
|
||||||
stop_time = now.replace(
|
stop_time = now.replace(
|
||||||
hour=self._stop_time.hour, minute=self._stop_time.minute,
|
hour=self._stop_time.hour, minute=self._stop_time.minute,
|
||||||
second=0)
|
second=0)
|
||||||
|
|
||||||
|
if stop_time <= start_time:
|
||||||
|
# stop_time does not happen in the same day as start_time
|
||||||
|
if start_time < now:
|
||||||
|
# stop time is tomorrow
|
||||||
|
stop_time += datetime.timedelta(days=1)
|
||||||
|
elif now < start_time:
|
||||||
|
# stop_time was yesterday since the new start_time is not reached
|
||||||
|
stop_time -= datetime.timedelta(days=1)
|
||||||
|
|
||||||
if start_time < now < sunset:
|
if start_time < now < sunset:
|
||||||
# Daytime
|
# Daytime
|
||||||
time_state = 'day'
|
time_state = 'day'
|
||||||
@ -192,15 +203,24 @@ class FluxSwitch(SwitchDevice):
|
|||||||
else:
|
else:
|
||||||
# Nightime
|
# Nightime
|
||||||
time_state = 'night'
|
time_state = 'night'
|
||||||
if now < stop_time and now > start_time:
|
|
||||||
now_time = now
|
if now < stop_time:
|
||||||
|
if stop_time < start_time and stop_time.day == sunset.day:
|
||||||
|
# we need to use yesterday's sunset time
|
||||||
|
sunset_time = sunset - datetime.timedelta(days=1)
|
||||||
else:
|
else:
|
||||||
now_time = stop_time
|
sunset_time = sunset
|
||||||
temp_range = abs(self._sunset_colortemp - self._stop_colortemp)
|
|
||||||
night_length = int(stop_time.timestamp() - sunset.timestamp())
|
# pylint: disable=no-member
|
||||||
seconds_from_sunset = int(now_time.timestamp() -
|
night_length = int(stop_time.timestamp() -
|
||||||
sunset.timestamp())
|
sunset_time.timestamp())
|
||||||
|
seconds_from_sunset = int(now.timestamp() -
|
||||||
|
sunset_time.timestamp())
|
||||||
percentage_complete = seconds_from_sunset / night_length
|
percentage_complete = seconds_from_sunset / night_length
|
||||||
|
else:
|
||||||
|
percentage_complete = 1
|
||||||
|
|
||||||
|
temp_range = abs(self._sunset_colortemp - self._stop_colortemp)
|
||||||
temp_offset = temp_range * percentage_complete
|
temp_offset = temp_range * percentage_complete
|
||||||
if self._sunset_colortemp > self._stop_colortemp:
|
if self._sunset_colortemp > self._stop_colortemp:
|
||||||
temp = self._sunset_colortemp - temp_offset
|
temp = self._sunset_colortemp - temp_offset
|
||||||
|
@ -347,6 +347,261 @@ class TestSwitchFlux(unittest.TestCase):
|
|||||||
self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 154)
|
self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 154)
|
||||||
self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.494, 0.397])
|
self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.494, 0.397])
|
||||||
|
|
||||||
|
def test_flux_before_sunrise_stop_next_day(self):
|
||||||
|
"""Test the flux switch before sunrise.
|
||||||
|
|
||||||
|
This test has the stop_time on the next day (after midnight).
|
||||||
|
"""
|
||||||
|
platform = loader.get_component('light.test')
|
||||||
|
platform.init()
|
||||||
|
self.assertTrue(
|
||||||
|
setup_component(self.hass, light.DOMAIN,
|
||||||
|
{light.DOMAIN: {CONF_PLATFORM: 'test'}}))
|
||||||
|
|
||||||
|
dev1 = platform.DEVICES[0]
|
||||||
|
|
||||||
|
# Verify initial state of light
|
||||||
|
state = self.hass.states.get(dev1.entity_id)
|
||||||
|
self.assertEqual(STATE_ON, state.state)
|
||||||
|
self.assertIsNone(state.attributes.get('xy_color'))
|
||||||
|
self.assertIsNone(state.attributes.get('brightness'))
|
||||||
|
|
||||||
|
test_time = dt_util.now().replace(hour=2, minute=30, second=0)
|
||||||
|
sunset_time = test_time.replace(hour=17, minute=0, second=0)
|
||||||
|
sunrise_time = test_time.replace(hour=5, minute=0, second=0)
|
||||||
|
|
||||||
|
def event_date(hass, event, now=None):
|
||||||
|
if event == 'sunrise':
|
||||||
|
return sunrise_time
|
||||||
|
else:
|
||||||
|
return sunset_time
|
||||||
|
|
||||||
|
with patch('homeassistant.util.dt.now', return_value=test_time):
|
||||||
|
with patch('homeassistant.helpers.sun.get_astral_event_date',
|
||||||
|
side_effect=event_date):
|
||||||
|
assert setup_component(self.hass, switch.DOMAIN, {
|
||||||
|
switch.DOMAIN: {
|
||||||
|
'platform': 'flux',
|
||||||
|
'name': 'flux',
|
||||||
|
'lights': [dev1.entity_id],
|
||||||
|
'stop_time': '01:00'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
turn_on_calls = mock_service(
|
||||||
|
self.hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
|
switch.turn_on(self.hass, 'switch.flux')
|
||||||
|
self.hass.block_till_done()
|
||||||
|
fire_time_changed(self.hass, test_time)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
call = turn_on_calls[-1]
|
||||||
|
self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 119)
|
||||||
|
self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.591, 0.395])
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def test_flux_after_sunrise_before_sunset_stop_next_day(self):
|
||||||
|
"""
|
||||||
|
Test the flux switch after sunrise and before sunset.
|
||||||
|
|
||||||
|
This test has the stop_time on the next day (after midnight).
|
||||||
|
"""
|
||||||
|
platform = loader.get_component('light.test')
|
||||||
|
platform.init()
|
||||||
|
self.assertTrue(
|
||||||
|
setup_component(self.hass, light.DOMAIN,
|
||||||
|
{light.DOMAIN: {CONF_PLATFORM: 'test'}}))
|
||||||
|
|
||||||
|
dev1 = platform.DEVICES[0]
|
||||||
|
|
||||||
|
# Verify initial state of light
|
||||||
|
state = self.hass.states.get(dev1.entity_id)
|
||||||
|
self.assertEqual(STATE_ON, state.state)
|
||||||
|
self.assertIsNone(state.attributes.get('xy_color'))
|
||||||
|
self.assertIsNone(state.attributes.get('brightness'))
|
||||||
|
|
||||||
|
test_time = dt_util.now().replace(hour=8, minute=30, second=0)
|
||||||
|
sunset_time = test_time.replace(hour=17, minute=0, second=0)
|
||||||
|
sunrise_time = test_time.replace(hour=5, minute=0, second=0)
|
||||||
|
|
||||||
|
def event_date(hass, event, now=None):
|
||||||
|
if event == 'sunrise':
|
||||||
|
return sunrise_time
|
||||||
|
else:
|
||||||
|
return sunset_time
|
||||||
|
|
||||||
|
with patch('homeassistant.util.dt.now', return_value=test_time):
|
||||||
|
with patch('homeassistant.helpers.sun.get_astral_event_date',
|
||||||
|
side_effect=event_date):
|
||||||
|
assert setup_component(self.hass, switch.DOMAIN, {
|
||||||
|
switch.DOMAIN: {
|
||||||
|
'platform': 'flux',
|
||||||
|
'name': 'flux',
|
||||||
|
'lights': [dev1.entity_id],
|
||||||
|
'stop_time': '01:00'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
turn_on_calls = mock_service(
|
||||||
|
self.hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
|
switch.turn_on(self.hass, 'switch.flux')
|
||||||
|
self.hass.block_till_done()
|
||||||
|
fire_time_changed(self.hass, test_time)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
call = turn_on_calls[-1]
|
||||||
|
self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 180)
|
||||||
|
self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.431, 0.38])
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def test_flux_after_sunset_before_midnight_stop_next_day(self):
|
||||||
|
"""Test the flux switch after sunset and before stop.
|
||||||
|
|
||||||
|
This test has the stop_time on the next day (after midnight).
|
||||||
|
"""
|
||||||
|
platform = loader.get_component('light.test')
|
||||||
|
platform.init()
|
||||||
|
self.assertTrue(
|
||||||
|
setup_component(self.hass, light.DOMAIN,
|
||||||
|
{light.DOMAIN: {CONF_PLATFORM: 'test'}}))
|
||||||
|
|
||||||
|
dev1 = platform.DEVICES[0]
|
||||||
|
|
||||||
|
# Verify initial state of light
|
||||||
|
state = self.hass.states.get(dev1.entity_id)
|
||||||
|
self.assertEqual(STATE_ON, state.state)
|
||||||
|
self.assertIsNone(state.attributes.get('xy_color'))
|
||||||
|
self.assertIsNone(state.attributes.get('brightness'))
|
||||||
|
|
||||||
|
test_time = dt_util.now().replace(hour=23, minute=30, second=0)
|
||||||
|
sunset_time = test_time.replace(hour=17, minute=0, second=0)
|
||||||
|
sunrise_time = test_time.replace(hour=5, minute=0, second=0)
|
||||||
|
|
||||||
|
def event_date(hass, event, now=None):
|
||||||
|
if event == 'sunrise':
|
||||||
|
return sunrise_time
|
||||||
|
else:
|
||||||
|
return sunset_time
|
||||||
|
|
||||||
|
with patch('homeassistant.util.dt.now', return_value=test_time):
|
||||||
|
with patch('homeassistant.helpers.sun.get_astral_event_date',
|
||||||
|
side_effect=event_date):
|
||||||
|
assert setup_component(self.hass, switch.DOMAIN, {
|
||||||
|
switch.DOMAIN: {
|
||||||
|
'platform': 'flux',
|
||||||
|
'name': 'flux',
|
||||||
|
'lights': [dev1.entity_id],
|
||||||
|
'stop_time': '01:00'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
turn_on_calls = mock_service(
|
||||||
|
self.hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
|
switch.turn_on(self.hass, 'switch.flux')
|
||||||
|
self.hass.block_till_done()
|
||||||
|
fire_time_changed(self.hass, test_time)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
call = turn_on_calls[-1]
|
||||||
|
self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 126)
|
||||||
|
self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.574, 0.401])
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def test_flux_after_sunset_after_midnight_stop_next_day(self):
|
||||||
|
"""Test the flux switch after sunset and before stop.
|
||||||
|
|
||||||
|
This test has the stop_time on the next day (after midnight).
|
||||||
|
"""
|
||||||
|
platform = loader.get_component('light.test')
|
||||||
|
platform.init()
|
||||||
|
self.assertTrue(
|
||||||
|
setup_component(self.hass, light.DOMAIN,
|
||||||
|
{light.DOMAIN: {CONF_PLATFORM: 'test'}}))
|
||||||
|
|
||||||
|
dev1 = platform.DEVICES[0]
|
||||||
|
|
||||||
|
# Verify initial state of light
|
||||||
|
state = self.hass.states.get(dev1.entity_id)
|
||||||
|
self.assertEqual(STATE_ON, state.state)
|
||||||
|
self.assertIsNone(state.attributes.get('xy_color'))
|
||||||
|
self.assertIsNone(state.attributes.get('brightness'))
|
||||||
|
|
||||||
|
test_time = dt_util.now().replace(hour=00, minute=30, second=0)
|
||||||
|
sunset_time = test_time.replace(hour=17, minute=0, second=0)
|
||||||
|
sunrise_time = test_time.replace(hour=5, minute=0, second=0)
|
||||||
|
|
||||||
|
def event_date(hass, event, now=None):
|
||||||
|
if event == 'sunrise':
|
||||||
|
return sunrise_time
|
||||||
|
else:
|
||||||
|
return sunset_time
|
||||||
|
|
||||||
|
with patch('homeassistant.util.dt.now', return_value=test_time):
|
||||||
|
with patch('homeassistant.helpers.sun.get_astral_event_date',
|
||||||
|
side_effect=event_date):
|
||||||
|
assert setup_component(self.hass, switch.DOMAIN, {
|
||||||
|
switch.DOMAIN: {
|
||||||
|
'platform': 'flux',
|
||||||
|
'name': 'flux',
|
||||||
|
'lights': [dev1.entity_id],
|
||||||
|
'stop_time': '01:00'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
turn_on_calls = mock_service(
|
||||||
|
self.hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
|
switch.turn_on(self.hass, 'switch.flux')
|
||||||
|
self.hass.block_till_done()
|
||||||
|
fire_time_changed(self.hass, test_time)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
call = turn_on_calls[-1]
|
||||||
|
self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 122)
|
||||||
|
self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.586, 0.397])
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
def test_flux_after_stop_before_sunrise_stop_next_day(self):
|
||||||
|
"""Test the flux switch after stop and before sunrise.
|
||||||
|
|
||||||
|
This test has the stop_time on the next day (after midnight).
|
||||||
|
"""
|
||||||
|
platform = loader.get_component('light.test')
|
||||||
|
platform.init()
|
||||||
|
self.assertTrue(
|
||||||
|
setup_component(self.hass, light.DOMAIN,
|
||||||
|
{light.DOMAIN: {CONF_PLATFORM: 'test'}}))
|
||||||
|
|
||||||
|
dev1 = platform.DEVICES[0]
|
||||||
|
|
||||||
|
# Verify initial state of light
|
||||||
|
state = self.hass.states.get(dev1.entity_id)
|
||||||
|
self.assertEqual(STATE_ON, state.state)
|
||||||
|
self.assertIsNone(state.attributes.get('xy_color'))
|
||||||
|
self.assertIsNone(state.attributes.get('brightness'))
|
||||||
|
|
||||||
|
test_time = dt_util.now().replace(hour=2, minute=30, second=0)
|
||||||
|
sunset_time = test_time.replace(hour=17, minute=0, second=0)
|
||||||
|
sunrise_time = test_time.replace(hour=5, minute=0, second=0)
|
||||||
|
|
||||||
|
def event_date(hass, event, now=None):
|
||||||
|
if event == 'sunrise':
|
||||||
|
return sunrise_time
|
||||||
|
else:
|
||||||
|
return sunset_time
|
||||||
|
|
||||||
|
with patch('homeassistant.util.dt.now', return_value=test_time):
|
||||||
|
with patch('homeassistant.helpers.sun.get_astral_event_date',
|
||||||
|
side_effect=event_date):
|
||||||
|
assert setup_component(self.hass, switch.DOMAIN, {
|
||||||
|
switch.DOMAIN: {
|
||||||
|
'platform': 'flux',
|
||||||
|
'name': 'flux',
|
||||||
|
'lights': [dev1.entity_id],
|
||||||
|
'stop_time': '01:00'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
turn_on_calls = mock_service(
|
||||||
|
self.hass, light.DOMAIN, SERVICE_TURN_ON)
|
||||||
|
switch.turn_on(self.hass, 'switch.flux')
|
||||||
|
self.hass.block_till_done()
|
||||||
|
fire_time_changed(self.hass, test_time)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
call = turn_on_calls[-1]
|
||||||
|
self.assertEqual(call.data[light.ATTR_BRIGHTNESS], 119)
|
||||||
|
self.assertEqual(call.data[light.ATTR_XY_COLOR], [0.591, 0.395])
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
def test_flux_with_custom_colortemps(self):
|
def test_flux_with_custom_colortemps(self):
|
||||||
"""Test the flux with custom start and stop colortemps."""
|
"""Test the flux with custom start and stop colortemps."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user