diff --git a/homeassistant/components/utility_meter/__init__.py b/homeassistant/components/utility_meter/__init__.py index 2f062851ee6..97321c456e5 100644 --- a/homeassistant/components/utility_meter/__init__.py +++ b/homeassistant/components/utility_meter/__init__.py @@ -1,5 +1,6 @@ """Support for tracking consumption over given periods of time.""" import logging +from datetime import timedelta import voluptuous as vol @@ -23,6 +24,8 @@ TARIFF_ICON = 'mdi:clock-outline' ATTR_TARIFFS = 'tariffs' +DEFAULT_OFFSET = timedelta(hours=0) + SERVICE_METER_SCHEMA = vol.Schema({ vol.Required(ATTR_ENTITY_ID): cv.entity_ids, }) @@ -35,7 +38,8 @@ METER_CONFIG_SCHEMA = vol.Schema({ vol.Required(CONF_SOURCE_SENSOR): cv.entity_id, vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_METER_TYPE): vol.In(METER_TYPES), - vol.Optional(CONF_METER_OFFSET, default=0): cv.positive_int, + vol.Optional(CONF_METER_OFFSET, default=DEFAULT_OFFSET): + vol.All(cv.time_period, cv.positive_timedelta), vol.Optional(CONF_METER_NET_CONSUMPTION, default=False): cv.boolean, vol.Optional(CONF_TARIFFS, default=[]): vol.All( cv.ensure_list, [cv.string]), diff --git a/homeassistant/components/utility_meter/sensor.py b/homeassistant/components/utility_meter/sensor.py index 21dc1099442..dd1514f5e43 100644 --- a/homeassistant/components/utility_meter/sensor.py +++ b/homeassistant/components/utility_meter/sensor.py @@ -1,6 +1,6 @@ """Utility meter from sensors providing raw data.""" import logging - +from datetime import date, timedelta from decimal import Decimal, DecimalException import homeassistant.util.dt as dt_util @@ -128,15 +128,17 @@ class UtilityMeterSensor(RestoreEntity): self.async_schedule_update_ha_state() async def _async_reset_meter(self, event): - """Determine cycle - Helper function for larger then daily cycles.""" - now = dt_util.now() - if self._period == WEEKLY and now.weekday() != self._period_offset: + """Determine cycle - Helper function for larger than daily cycles.""" + now = dt_util.now().date() + if self._period == WEEKLY and\ + now != now - timedelta(days=now.weekday())\ + + self._period_offset: return if self._period == MONTHLY and\ - now.day != (1 + self._period_offset): + now != date(now.year, now.month, 1) + self._period_offset: return if self._period == YEARLY and\ - (now.month != (1 + self._period_offset) or now.day != 1): + now != date(now.year, 1, 1) + self._period_offset: return await self.async_reset_meter(self._tariff_entity) @@ -155,15 +157,16 @@ class UtilityMeterSensor(RestoreEntity): await super().async_added_to_hass() if self._period == HOURLY: - async_track_time_change(self.hass, self._async_reset_meter, - minute=self._period_offset, second=0) - elif self._period == DAILY: - async_track_time_change(self.hass, self._async_reset_meter, - hour=self._period_offset, minute=0, - second=0) - elif self._period in [WEEKLY, MONTHLY, YEARLY]: - async_track_time_change(self.hass, self._async_reset_meter, - hour=0, minute=0, second=0) + async_track_time_change( + self.hass, self._async_reset_meter, + minute=self._period_offset.seconds // 60, + second=self._period_offset.seconds % 60) + elif self._period in [DAILY, WEEKLY, MONTHLY, YEARLY]: + async_track_time_change( + self.hass, self._async_reset_meter, + hour=self._period_offset.seconds // 3600, + minute=self._period_offset.seconds % 3600 // 60, + second=self._period_offset.seconds % 3600 % 60) async_dispatcher_connect( self.hass, SIGNAL_RESET_METER, self.async_reset_meter) diff --git a/tests/components/utility_meter/test_sensor.py b/tests/components/utility_meter/test_sensor.py index 03c95fdf897..ee291439a2c 100644 --- a/tests/components/utility_meter/test_sensor.py +++ b/tests/components/utility_meter/test_sensor.py @@ -123,8 +123,8 @@ async def test_non_net_consumption(hass): assert state.state == '0' -async def _test_self_reset(hass, cycle, start_time, expect_reset=True): - """Test energy sensor self reset.""" +def gen_config(cycle, offset=None): + """Generate configuration.""" config = { 'utility_meter': { 'energy_bill': { @@ -134,6 +134,16 @@ async def _test_self_reset(hass, cycle, start_time, expect_reset=True): } } + if offset: + config['utility_meter']['energy_bill']['offset'] = { + 'days': offset.days, + 'seconds': offset.seconds + } + return config + + +async def _test_self_reset(hass, config, start_time, expect_reset=True): + """Test energy sensor self reset.""" assert await async_setup_component(hass, DOMAIN, config) assert await async_setup_component(hass, SENSOR_DOMAIN, config) await hass.async_block_till_done() @@ -173,30 +183,50 @@ async def _test_self_reset(hass, cycle, start_time, expect_reset=True): async def test_self_reset_hourly(hass): """Test hourly reset of meter.""" - await _test_self_reset(hass, 'hourly', "2017-12-31T23:59:00.000000+00:00") + await _test_self_reset(hass, gen_config('hourly'), + "2017-12-31T23:59:00.000000+00:00") async def test_self_reset_daily(hass): """Test daily reset of meter.""" - await _test_self_reset(hass, 'daily', "2017-12-31T23:59:00.000000+00:00") + await _test_self_reset(hass, gen_config('daily'), + "2017-12-31T23:59:00.000000+00:00") async def test_self_reset_weekly(hass): """Test weekly reset of meter.""" - await _test_self_reset(hass, 'weekly', "2017-12-31T23:59:00.000000+00:00") + await _test_self_reset(hass, gen_config('weekly'), + "2017-12-31T23:59:00.000000+00:00") async def test_self_reset_monthly(hass): """Test monthly reset of meter.""" - await _test_self_reset(hass, 'monthly', "2017-12-31T23:59:00.000000+00:00") + await _test_self_reset(hass, gen_config('monthly'), + "2017-12-31T23:59:00.000000+00:00") async def test_self_reset_yearly(hass): """Test yearly reset of meter.""" - await _test_self_reset(hass, 'yearly', "2017-12-31T23:59:00.000000+00:00") + await _test_self_reset(hass, gen_config('yearly'), + "2017-12-31T23:59:00.000000+00:00") async def test_self_no_reset_yearly(hass): """Test yearly reset of meter does not occur after 1st January.""" - await _test_self_reset(hass, 'yearly', "2018-01-01T23:59:00.000000+00:00", + await _test_self_reset(hass, gen_config('yearly'), + "2018-01-01T23:59:00.000000+00:00", + expect_reset=False) + + +async def test_reset_yearly_offset(hass): + """Test yearly reset of meter.""" + await _test_self_reset(hass, + gen_config('yearly', timedelta(days=1, minutes=10)), + "2018-01-02T00:09:00.000000+00:00") + + +async def test_no_reset_yearly_offset(hass): + """Test yearly reset of meter.""" + await _test_self_reset(hass, gen_config('yearly', timedelta(31)), + "2018-01-30T23:59:00.000000+00:00", expect_reset=False)