Skip to content

Commit

Permalink
Merge pull request #164 from bruxy70/development
Browse files Browse the repository at this point in the history
3.0 release candidate
  • Loading branch information
bruxy70 authored Jun 5, 2020
2 parents 9f79c38 + 97bc2a7 commit 355cd9b
Show file tree
Hide file tree
Showing 22 changed files with 381 additions and 110 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Entity_id change is not possible using the YAML configuration. Changing other pa
|:----------|----------|------------
| `name` | Yes | Sensor friendly name
| `frequency` | Yes | `"weekly"`, `"even-weeks"`, `"odd-weeks"`, `"every-n-weeks"`, `"every-n-days"`, `"monthly"`, `"annual"` or `"group"`
| `hide` | No | Hide in calendar (useful for sensors that are used in groups)<br/>**Default**: `False`
| `icon_normal` | No | Default icon **Default**: `mdi:trash-can`
| `icon_today` | No | Icon if the collection is today **Default**: `mdi:delete-restore`
| `icon_tomorrow` | No | Icon if the collection is tomorrow **Default**: `mdi:delete-circle`
Expand All @@ -127,7 +128,8 @@ Entity_id change is not possible using the YAML configuration. Changing other pa
| `last_month` | No | Month three letter abbreviation.<br/>**Default**: `"dec"`
| `exclude_dates` | No | List of dates with no collection (using international date format `'yyyy-mm-dd'`.
| `include_dates` | No | List of extra collection (using international date format `'yyyy-mm-dd'`.
| `move_country_holidays` | No | Country holidays - the country code (see [holidays](https://github.com/dr-prodigy/python-holidays) for the list of valid country codes).<br/>Automatically move garbage collection on public holidays to the following day.<br/>*Example:* `US`
| `move_country_holidays` | No | Country holidays - the country code (see [holidays](https://github.com/dr-prodigy/python-holidays) for the list of valid country codes).<br/>Automatically move garbage collection on public holidays to the following day.<br/>*Example:* `US`
| `holiday_in_week_move` | No | Move garbage collection to the following day if a holiday is in week.<br/>**Default**: `false`
| `prov` | No | Country holidays - province (see [holidays](https://github.com/dr-prodigy/python-holidays) ).
| `state` | No | Country holidays - state (see [holidays](https://github.com/dr-prodigy/python-holidays) ).
| `observed` | No | Country holidays - observed (see [holidays](https://github.com/dr-prodigy/python-holidays) ).
Expand Down
36 changes: 27 additions & 9 deletions custom_components/garbage_collection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@
from homeassistant.helpers import discovery
from homeassistant.util import Throttle
from .sensor import GarbageCollection
from .calendar import EntitiesCalendarData

from integrationhelper.const import CC_STARTUP_VERSION

from homeassistant.const import CONF_NAME

from .const import (
CONF_SENSORS,
CALENDAR_NAME,
CONF_ENABLED,
CONF_FREQUENCY,
DEFAULT_NAME,
DOMAIN_DATA,
DOMAIN,
ISSUE_URL,
PLATFORM,
SENSOR_PLATFORM,
CALENDAR_PLATFORM,
VERSION,
CONFIG_SCHEMA,
)
Expand All @@ -35,6 +36,17 @@

async def async_setup(hass, config):
"""Set up this component using YAML."""
# Create calendar
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}
if CALENDAR_PLATFORM not in hass.data[DOMAIN]:
hass.data[DOMAIN][CALENDAR_PLATFORM] = EntitiesCalendarData(hass)
_LOGGER.debug("Creating calendar")
hass.async_create_task(
discovery.async_load_platform(
hass, CALENDAR_PLATFORM, DOMAIN, {"name": CALENDAR_NAME}, config,
)
)
if config.get(DOMAIN) is None:
# We get here if the integration is set up using config flow
return True
Expand All @@ -57,7 +69,7 @@ async def async_setup(hass, config):
# if not entry[CONF_ENABLED]:
# continue
hass.async_create_task(
discovery.async_load_platform(hass, PLATFORM, DOMAIN, entry, config)
discovery.async_load_platform(hass, SENSOR_PLATFORM, DOMAIN, entry, config)
)
hass.async_create_task(
hass.config_entries.flow.async_init(
Expand All @@ -77,21 +89,25 @@ async def async_setup_entry(hass, config_entry):
_LOGGER.info(
CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL)
)
_LOGGER.info(f"Setting {config_entry.title}({config_entry.data[CONF_FREQUENCY]}) from ConfigFlow")
_LOGGER.info(
f"Setting {config_entry.title}({config_entry.data[CONF_FREQUENCY]}) from ConfigFlow"
)
# Backward compatibility - clean-up (can be removed later?)
config_entry.options = {}
config_entry.add_update_listener(update_listener)
# Add sensor
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(config_entry, PLATFORM)
hass.config_entries.async_forward_entry_setup(config_entry, SENSOR_PLATFORM)
)
return True


async def async_remove_entry(hass, config_entry):
"""Handle removal of an entry."""
try:
await hass.config_entries.async_forward_entry_unload(config_entry, PLATFORM)
await hass.config_entries.async_forward_entry_unload(
config_entry, SENSOR_PLATFORM
)
_LOGGER.info(
"Successfully removed sensor from the garbage_collection integration"
)
Expand All @@ -105,5 +121,7 @@ async def update_listener(hass, entry):
if entry.options != {}:
entry.data = entry.options
entry.options = {}
await hass.config_entries.async_forward_entry_unload(entry, PLATFORM)
hass.async_add_job(hass.config_entries.async_forward_entry_setup(entry, PLATFORM))
await hass.config_entries.async_forward_entry_unload(entry, SENSOR_PLATFORM)
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(entry, SENSOR_PLATFORM)
)
142 changes: 142 additions & 0 deletions custom_components/garbage_collection/calendar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""Garbage Collection Callendar"""
from datetime import datetime, timedelta
import logging
from homeassistant.components.calendar import PLATFORM_SCHEMA, CalendarEventDevice
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.template import DATE_STR_FORMAT
from homeassistant.util import Throttle, dt

# from .sensor import find_entity

from .const import (
DOMAIN,
CALENDAR_PLATFORM,
SENSOR_PLATFORM,
CALENDAR_NAME,
)

_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)


async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None
): # pylint: disable=unused-argument
"""Setup the calendar platform."""
# Only single instance allowed
if GarbageCollectionCalendar.instances == 0:
async_add_entities([GarbageCollectionCalendar(hass)], True)


class GarbageCollectionCalendar(CalendarEventDevice):
"""The garbage collection calendar class"""

instances = 0

def __init__(self, hass):
"""Create empry calendar"""
self._cal_data = {}
self._name = CALENDAR_NAME
GarbageCollectionCalendar.instances += 1

@property
def event(self):
"""Return the next upcoming event."""
return self.hass.data[DOMAIN][CALENDAR_PLATFORM].event

@property
def name(self):
"""Return the name of the entity."""
return self._name

async def async_update(self):
"""Update all calendars."""
self.hass.data[DOMAIN][CALENDAR_PLATFORM].update()

async def async_get_events(self, hass, start_date, end_date):
"""Get all events in a specific time frame."""
return await self.hass.data[DOMAIN][CALENDAR_PLATFORM].async_get_events(
hass, start_date, end_date
)

@property
def device_state_attributes(self):
"""Return the device state attributes."""
if self.hass.data[DOMAIN][CALENDAR_PLATFORM].event is None:
# No tasks, we don't need to show anything.
return None

return {}


class EntitiesCalendarData:
"""
Class used by the Entities Calendar class to hold all entity events.
"""

def __init__(self, hass):
"""Initialize an Entities Calendar Data"""
self.event = None
self._hass = hass
self.entities = []

def add_entity(self, entity_id):
"""Append entity ID to the calendar"""
if entity_id not in self.entities:
self.entities.append(entity_id)

def remove_entity(self, entity_id):
"""Remove entity ID from the calendar"""
if entity_id in self.entities:
self.entities.remove(entity_id)

async def async_get_events(self, hass, start_datetime, end_datetime):
"""Get all tasks in a specific time frame."""
events = []
if SENSOR_PLATFORM not in hass.data[DOMAIN]:
return events
start_date = start_datetime.date()
end_date = end_datetime.date()
for entity in self.entities:
# garbage_collection = find_entity(hass, entity)
if (
entity not in hass.data[DOMAIN][SENSOR_PLATFORM]
or hass.data[DOMAIN][SENSOR_PLATFORM][entity].hidden
):
continue
garbage_collection = hass.data[DOMAIN][SENSOR_PLATFORM][entity]
await garbage_collection.async_load_holidays(start_date)
start = await garbage_collection.async_find_next_date(start_date)
while start is not None and start >= start_date and start <= end_date:
event = {
"uid": entity,
"summary": garbage_collection.name,
"start": {"date": start.strftime("%Y-%m-%d"),},
"end": {"date": start.strftime("%Y-%m-%d"),},
"allDay": True,
}
events.append(event)
start = await garbage_collection.async_find_next_date(
start + timedelta(days=1)
)
return events

@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data."""
events = []
for entity in self.entities:
state_object = self._hass.states.get(entity)
start = state_object.attributes.get("next_date")
if start is not None:
event = {
"uid": entity,
"summary": state_object.attributes.get("friendly_name"),
"start": {"date": start.strftime("%Y-%m-%d"),},
"end": {"date": start.strftime("%Y-%m-%d"),},
"allDay": True,
}
events.append(event)
events.sort(key=lambda x: x["start"]["date"])
if len(events) > 0:
self.event = events[0]
4 changes: 1 addition & 3 deletions custom_components/garbage_collection/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,7 @@ async def async_step_detail(
"""
C O N F I G U R A T I O N S T E P 2 ( O T H E R T H A N A N N U A L O R G R O U P )
"""
next_step = self.shared_class.step3_detail(
user_input, self.config_entry.data
)
next_step = self.shared_class.step3_detail(user_input, self.config_entry.data)
if next_step:
return await self.async_step_final()
else:
Expand Down
26 changes: 22 additions & 4 deletions custom_components/garbage_collection/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
from .config_singularity import config_singularity
from datetime import datetime, date
import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_NAME, WEEKDAYS, CONF_ENTITIES
from homeassistant.const import CONF_NAME, WEEKDAYS, CONF_ENTITIES, ATTR_HIDDEN

"""Constants for garbage_collection."""
# Base component constants
DOMAIN = "garbage_collection"
DOMAIN_DATA = f"{DOMAIN}_data"
VERSION = "2.28"
PLATFORM = "sensor"
CALENDAR_NAME = "Garbage Collection"
VERSION = "3.00"
SENSOR_PLATFORM = "sensor"
CALENDAR_PLATFORM = "calendar"
ISSUE_URL = "https://github.com/bruxy70/Garbage-Collection/issues"
ATTRIBUTION = "Data from this is provided by garbage_collection."

Expand All @@ -18,6 +19,7 @@

# Device classes
BINARY_SENSOR_DEVICE_CLASS = "connectivity"
DEVICE_CLASS = "garbage_collection__schedule"

# Configuration
CONF_SENSOR = "sensor"
Expand All @@ -38,6 +40,7 @@
CONF_EXCLUDE_DATES = "exclude_dates"
CONF_INCLUDE_DATES = "include_dates"
CONF_MOVE_COUNTRY_HOLIDAYS = "move_country_holidays"
CONF_HOLIDAY_IN_WEEK_MOVE = "holiday_in_week_move"
CONF_PROV = "prov"
CONF_STATE = "state"
CONF_OBSERVED = "observed"
Expand All @@ -56,6 +59,7 @@
DEFAULT_PERIOD = 1
DEFAULT_FIRST_WEEK = 1
DEFAULT_VERBOSE_STATE = False
DEFAULT_HOLIDAY_IN_WEEK_MOVE = False
DEFAULT_DATE_FORMAT = "%d-%b-%Y"
DEFAULT_VERBOSE_FORMAT = "on {date}, in {days} days"

Expand Down Expand Up @@ -208,6 +212,13 @@ class configuration(config_singularity):
"type": str,
"validator": cv.string,
},
ATTR_HIDDEN: {
"step": 1,
"method": vol.Optional,
"default": False,
"type": bool,
"validator": cv.boolean,
},
CONF_FREQUENCY: {
"step": 1,
"method": vol.Required,
Expand Down Expand Up @@ -356,6 +367,13 @@ class configuration(config_singularity):
"method": vol.Optional,
"type": vol.In(COUNTRY_CODES),
},
CONF_HOLIDAY_IN_WEEK_MOVE: {
"step": 4,
"method": vol.Optional,
"default": DEFAULT_HOLIDAY_IN_WEEK_MOVE,
"type": bool,
"validator": cv.boolean,
},
CONF_PROV: {
"step": 4,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP,
Expand Down
1 change: 1 addition & 0 deletions custom_components/garbage_collection/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"domain": "garbage_collection",
"name": "Garbage Collection",
"documentation": "https://github.com/bruxy70/Garbage-Collection/",
"issue_tracker": "https://github.com/bruxy70/Garbage-Collection/issues",
"dependencies": [],
"config_flow": true,
"codeowners": [
Expand Down
Loading

0 comments on commit 355cd9b

Please sign in to comment.