Google Calendar round 2 (#4161)
* Google Calendar round 2 * Add google back to .coveragerc * Update __init__.py
This commit is contained in:
parent
f70ff66d11
commit
613615433a
11 changed files with 1160 additions and 1 deletions
|
@ -28,6 +28,9 @@ omit =
|
|||
homeassistant/components/envisalink.py
|
||||
homeassistant/components/*/envisalink.py
|
||||
|
||||
homeassistant/components/google.py
|
||||
homeassistant/components/*/google.py
|
||||
|
||||
homeassistant/components/insteon_hub.py
|
||||
homeassistant/components/*/insteon_hub.py
|
||||
|
||||
|
@ -132,7 +135,7 @@ omit =
|
|||
homeassistant/components/climate/knx.py
|
||||
homeassistant/components/climate/proliphix.py
|
||||
homeassistant/components/climate/radiotherm.py
|
||||
homeassistant/components/cover/garadget.py
|
||||
homeassistant/components/cover/garadget.py
|
||||
homeassistant/components/cover/homematic.py
|
||||
homeassistant/components/cover/rpi_gpio.py
|
||||
homeassistant/components/cover/scsgate.py
|
||||
|
|
183
homeassistant/components/calendar/__init__.py
Normal file
183
homeassistant/components/calendar/__init__.py
Normal file
|
@ -0,0 +1,183 @@
|
|||
"""
|
||||
Support for Google Calendar event device sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/calendar/
|
||||
|
||||
"""
|
||||
import logging
|
||||
import re
|
||||
|
||||
from homeassistant.components.google import (CONF_OFFSET,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_NAME)
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers.config_validation import time_period_str
|
||||
from homeassistant.helpers.entity import Entity, generate_entity_id
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.template import DATE_STR_FORMAT
|
||||
from homeassistant.util import dt
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'calendar'
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Track states and offer events for calendars."""
|
||||
component = EntityComponent(
|
||||
logging.getLogger(__name__), DOMAIN, hass, 60, DOMAIN)
|
||||
|
||||
component.setup(config)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
DEFAULT_CONF_TRACK_NEW = True
|
||||
DEFAULT_CONF_OFFSET = '!!'
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class CalendarEventDevice(Entity):
|
||||
"""A calendar event device."""
|
||||
|
||||
# Classes overloading this must set data to an object
|
||||
# with an update() method
|
||||
data = None
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, hass, data):
|
||||
"""Create the Calendar Event Device."""
|
||||
self._name = data.get(CONF_NAME)
|
||||
self.dev_id = data.get(CONF_DEVICE_ID)
|
||||
self._offset = data.get(CONF_OFFSET, DEFAULT_CONF_OFFSET)
|
||||
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT,
|
||||
self.dev_id,
|
||||
hass=hass)
|
||||
|
||||
self._cal_data = {
|
||||
'all_day': False,
|
||||
'offset_time': dt.dt.timedelta(),
|
||||
'message': '',
|
||||
'start': None,
|
||||
'end': None,
|
||||
'location': '',
|
||||
'description': '',
|
||||
}
|
||||
|
||||
self.update()
|
||||
|
||||
def offset_reached(self):
|
||||
"""Have we reached the offset time specified in the event title."""
|
||||
if self._cal_data['start'] is None or \
|
||||
self._cal_data['offset_time'] == dt.dt.timedelta():
|
||||
return False
|
||||
|
||||
return self._cal_data['start'] + self._cal_data['offset_time'] <= \
|
||||
dt.now(self._cal_data['start'].tzinfo)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the entity."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""State Attributes for HA."""
|
||||
start = self._cal_data.get('start', None)
|
||||
end = self._cal_data.get('end', None)
|
||||
start = start.strftime(DATE_STR_FORMAT) if start is not None else None
|
||||
end = end.strftime(DATE_STR_FORMAT) if end is not None else None
|
||||
|
||||
return {
|
||||
'message': self._cal_data.get('message', ''),
|
||||
'all_day': self._cal_data.get('all_day', False),
|
||||
'offset_reached': self.offset_reached(),
|
||||
'start_time': start,
|
||||
'end_time': end,
|
||||
'location': self._cal_data.get('location', None),
|
||||
'description': self._cal_data.get('description', None),
|
||||
}
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the calendar event."""
|
||||
start = self._cal_data.get('start', None)
|
||||
end = self._cal_data.get('end', None)
|
||||
if start is None or end is None:
|
||||
return STATE_OFF
|
||||
|
||||
now = dt.now()
|
||||
|
||||
if start <= now and end > now:
|
||||
return STATE_ON
|
||||
|
||||
if now >= end:
|
||||
self.cleanup()
|
||||
|
||||
return STATE_OFF
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup any start/end listeners that were setup."""
|
||||
self._cal_data = {
|
||||
'all_day': False,
|
||||
'offset_time': 0,
|
||||
'message': '',
|
||||
'start': None,
|
||||
'end': None,
|
||||
'location': None,
|
||||
'description': None
|
||||
}
|
||||
|
||||
def update(self):
|
||||
"""Search for the next event."""
|
||||
if not self.data or not self.data.update():
|
||||
# update cached, don't do anything
|
||||
return
|
||||
|
||||
if not self.data.event:
|
||||
# we have no event to work on, make sure we're clean
|
||||
self.cleanup()
|
||||
return
|
||||
|
||||
def _get_date(date):
|
||||
"""Get the dateTime from date or dateTime as a local."""
|
||||
if 'date' in date:
|
||||
return dt.as_utc(dt.dt.datetime.combine(
|
||||
dt.parse_date(date['date']), dt.dt.time()))
|
||||
else:
|
||||
return dt.parse_datetime(date['dateTime'])
|
||||
|
||||
start = _get_date(self.data.event['start'])
|
||||
end = _get_date(self.data.event['end'])
|
||||
|
||||
summary = self.data.event['summary']
|
||||
|
||||
# check if we have an offset tag in the message
|
||||
# time is HH:MM or MM
|
||||
reg = '{}([+-]?[0-9]{{0,2}}(:[0-9]{{0,2}})?)'.format(self._offset)
|
||||
search = re.search(reg, summary)
|
||||
if search and search.group(1):
|
||||
time = search.group(1)
|
||||
if ':' not in time:
|
||||
if time[0] == '+' or time[0] == '-':
|
||||
time = '{}0:{}'.format(time[0], time[1:])
|
||||
else:
|
||||
time = '0:{}'.format(time)
|
||||
|
||||
offset_time = time_period_str(time)
|
||||
summary = (summary[:search.start()] + summary[search.end():]) \
|
||||
.strip()
|
||||
else:
|
||||
offset_time = dt.dt.timedelta() # default it
|
||||
|
||||
# cleanup the string so we don't have a bunch of double+ spaces
|
||||
self._cal_data['message'] = re.sub(' +', '', summary).strip()
|
||||
|
||||
self._cal_data['offset_time'] = offset_time
|
||||
self._cal_data['location'] = self.data.event.get('location', '')
|
||||
self._cal_data['description'] = self.data.event.get('description', '')
|
||||
self._cal_data['start'] = start
|
||||
self._cal_data['end'] = end
|
||||
self._cal_data['all_day'] = 'date' in self.data.event['start']
|
82
homeassistant/components/calendar/demo.py
Executable file
82
homeassistant/components/calendar/demo.py
Executable file
|
@ -0,0 +1,82 @@
|
|||
"""
|
||||
Demo platform that has two fake binary sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation
|
||||
https://home-assistant.io/components/demo/
|
||||
"""
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.components.calendar import CalendarEventDevice
|
||||
from homeassistant.components.google import CONF_DEVICE_ID, CONF_NAME
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Demo binary sensor platform."""
|
||||
calendar_data_future = DemoGoogleCalendarDataFuture()
|
||||
calendar_data_current = DemoGoogleCalendarDataCurrent()
|
||||
add_devices([
|
||||
DemoGoogleCalendar(hass, calendar_data_future, {
|
||||
CONF_NAME: 'Future Event',
|
||||
CONF_DEVICE_ID: 'future_event',
|
||||
}),
|
||||
|
||||
DemoGoogleCalendar(hass, calendar_data_current, {
|
||||
CONF_NAME: 'Current Event',
|
||||
CONF_DEVICE_ID: 'current_event',
|
||||
}),
|
||||
])
|
||||
|
||||
|
||||
class DemoGoogleCalendarData(object):
|
||||
"""Setup base class for data."""
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def update(self):
|
||||
"""Return true so entity knows we have new data."""
|
||||
return True
|
||||
|
||||
|
||||
class DemoGoogleCalendarDataFuture(DemoGoogleCalendarData):
|
||||
"""Setup future data event."""
|
||||
|
||||
def __init__(self):
|
||||
"""Set the event to a future event."""
|
||||
one_hour_from_now = dt_util.now() \
|
||||
+ dt_util.dt.timedelta(minutes=30)
|
||||
self.event = {
|
||||
'start': {
|
||||
'dateTime': one_hour_from_now.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'dateTime': (one_hour_from_now + dt_util.dt.
|
||||
timedelta(minutes=60)).isoformat()
|
||||
},
|
||||
'summary': 'Future Event',
|
||||
}
|
||||
|
||||
|
||||
class DemoGoogleCalendarDataCurrent(DemoGoogleCalendarData):
|
||||
"""Create a current event we're in the middle of."""
|
||||
|
||||
def __init__(self):
|
||||
"""Set the event data."""
|
||||
middle_of_event = dt_util.now() \
|
||||
- dt_util.dt.timedelta(minutes=30)
|
||||
self.event = {
|
||||
'start': {
|
||||
'dateTime': middle_of_event.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'dateTime': (middle_of_event + dt_util.dt.
|
||||
timedelta(minutes=60)).isoformat()
|
||||
},
|
||||
'summary': 'Current Event',
|
||||
}
|
||||
|
||||
|
||||
class DemoGoogleCalendar(CalendarEventDevice):
|
||||
"""A Demo binary sensor."""
|
||||
|
||||
def __init__(self, hass, calendar_data, data):
|
||||
"""The same as a google calendar but without the api calls."""
|
||||
self.data = calendar_data
|
||||
super().__init__(hass, data)
|
79
homeassistant/components/calendar/google.py
Normal file
79
homeassistant/components/calendar/google.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
"""
|
||||
Support for Google Calendar Search binary sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/binary_sensor.google_calendar/
|
||||
"""
|
||||
# pylint: disable=import-error
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.components.calendar import CalendarEventDevice
|
||||
from homeassistant.components.google import (CONF_CAL_ID, CONF_ENTITIES,
|
||||
CONF_TRACK, TOKEN_FILE,
|
||||
GoogleCalendarService)
|
||||
from homeassistant.util import Throttle, dt
|
||||
|
||||
DEFAULT_GOOGLE_SEARCH_PARAMS = {
|
||||
'orderBy': 'startTime',
|
||||
'maxResults': 1,
|
||||
'singleEvents': True,
|
||||
}
|
||||
|
||||
# Return cached results if last scan was less then this time ago
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=15)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, disc_info=None):
|
||||
"""Setup the calendar platform for event devices."""
|
||||
if disc_info is None:
|
||||
return
|
||||
|
||||
if not any([data[CONF_TRACK] for data in disc_info[CONF_ENTITIES]]):
|
||||
return
|
||||
|
||||
calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE))
|
||||
add_devices([GoogleCalendarEventDevice(hass, calendar_service,
|
||||
disc_info[CONF_CAL_ID], data)
|
||||
for data in disc_info[CONF_ENTITIES] if data[CONF_TRACK]])
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class GoogleCalendarEventDevice(CalendarEventDevice):
|
||||
"""A calendar event device."""
|
||||
|
||||
def __init__(self, hass, calendar_service, calendar, data):
|
||||
"""Create the Calendar event device."""
|
||||
self.data = GoogleCalendarData(calendar_service, calendar,
|
||||
data.get('search', None))
|
||||
super().__init__(hass, data)
|
||||
|
||||
|
||||
class GoogleCalendarData(object):
|
||||
"""Class to utilize calendar service object to get next event."""
|
||||
|
||||
def __init__(self, calendar_service, calendar_id, search=None):
|
||||
"""Setup how we are going to search the google calendar."""
|
||||
self.calendar_service = calendar_service
|
||||
self.calendar_id = calendar_id
|
||||
self.search = search
|
||||
self.event = None
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Get the latest data."""
|
||||
service = self.calendar_service.get()
|
||||
params = dict(DEFAULT_GOOGLE_SEARCH_PARAMS)
|
||||
params['timeMin'] = dt.utcnow().isoformat('T')
|
||||
params['calendarId'] = self.calendar_id
|
||||
if self.search:
|
||||
params['q'] = self.search
|
||||
|
||||
events = service.events() # pylint: disable=no-member
|
||||
result = events.list(**params).execute()
|
||||
|
||||
items = result.get('items', [])
|
||||
self.event = items[0] if len(items) == 1 else None
|
||||
return True
|
|
@ -17,6 +17,7 @@ DOMAIN = 'demo'
|
|||
COMPONENTS_WITH_DEMO_PLATFORM = [
|
||||
'alarm_control_panel',
|
||||
'binary_sensor',
|
||||
'calendar',
|
||||
'camera',
|
||||
'climate',
|
||||
'cover',
|
||||
|
|
292
homeassistant/components/google.py
Normal file
292
homeassistant/components/google.py
Normal file
|
@ -0,0 +1,292 @@
|
|||
"""
|
||||
Support for Google - Calendar Event Devices.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/google/
|
||||
|
||||
NOTE TO OTHER DEVELOPERS: IF YOU ADD MORE SCOPES TO THE OAUTH THAN JUST
|
||||
CALENDAR THEN USERS WILL NEED TO DELETE THEIR TOKEN_FILE. THEY WILL LOSE THEIR
|
||||
REFRESH_TOKEN PIECE WHEN RE-AUTHENTICATING TO ADD MORE API ACCESS
|
||||
IT'S BEST TO JUST HAVE SEPARATE OAUTH FOR DIFFERENT PIECES OF GOOGLE
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import yaml
|
||||
|
||||
import voluptuous as vol
|
||||
from voluptuous.error import Error as VoluptuousError
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.loader as loader
|
||||
from homeassistant import bootstrap
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.helpers.entity import generate_entity_id
|
||||
from homeassistant.helpers.event import track_time_change
|
||||
from homeassistant.util import convert, dt
|
||||
|
||||
REQUIREMENTS = [
|
||||
'google-api-python-client==1.5.5',
|
||||
'oauth2client==3.0.0',
|
||||
]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'google'
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
CONF_CLIENT_ID = 'client_id'
|
||||
CONF_CLIENT_SECRET = 'client_secret'
|
||||
CONF_TRACK_NEW = 'track_new_calendar'
|
||||
|
||||
CONF_CAL_ID = 'cal_id'
|
||||
CONF_DEVICE_ID = 'device_id'
|
||||
CONF_NAME = 'name'
|
||||
CONF_ENTITIES = 'entities'
|
||||
CONF_TRACK = 'track'
|
||||
CONF_SEARCH = 'search'
|
||||
CONF_OFFSET = 'offset'
|
||||
|
||||
DEFAULT_CONF_TRACK_NEW = True
|
||||
DEFAULT_CONF_OFFSET = '!!'
|
||||
|
||||
NOTIFICATION_ID = 'google_calendar_notification'
|
||||
NOTIFICATION_TITLE = 'Google Calendar Setup'
|
||||
GROUP_NAME_ALL_CALENDARS = "Google Calendar Sensors"
|
||||
|
||||
SERVICE_SCAN_CALENDARS = 'scan_for_calendars'
|
||||
SERVICE_FOUND_CALENDARS = 'found_calendar'
|
||||
|
||||
DATA_INDEX = 'google_calendars'
|
||||
|
||||
YAML_DEVICES = '{}_calendars.yaml'.format(DOMAIN)
|
||||
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
|
||||
|
||||
TOKEN_FILE = '.{}.token'.format(DOMAIN)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Required(CONF_CLIENT_ID): cv.string,
|
||||
vol.Required(CONF_CLIENT_SECRET): cv.string,
|
||||
vol.Optional(CONF_TRACK_NEW): cv.boolean,
|
||||
})
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
_SINGLE_CALSEARCH_CONFIG = vol.Schema({
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_DEVICE_ID): cv.string,
|
||||
vol.Optional(CONF_TRACK): cv.boolean,
|
||||
vol.Optional(CONF_SEARCH): vol.Any(cv.string, None),
|
||||
vol.Optional(CONF_OFFSET): cv.string,
|
||||
})
|
||||
|
||||
DEVICE_SCHEMA = vol.Schema({
|
||||
vol.Required(CONF_CAL_ID): cv.string,
|
||||
vol.Required(CONF_ENTITIES, None):
|
||||
vol.All(cv.ensure_list, [_SINGLE_CALSEARCH_CONFIG]),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def do_authentication(hass, config):
|
||||
"""Notify user of actions and authenticate.
|
||||
|
||||
Notify user of user_code and verification_url then poll
|
||||
until we have an access token.
|
||||
"""
|
||||
from oauth2client.client import (
|
||||
OAuth2WebServerFlow,
|
||||
OAuth2DeviceCodeError,
|
||||
FlowExchangeError
|
||||
)
|
||||
from oauth2client.file import Storage
|
||||
|
||||
oauth = OAuth2WebServerFlow(
|
||||
config[CONF_CLIENT_ID],
|
||||
config[CONF_CLIENT_SECRET],
|
||||
'https://www.googleapis.com/auth/calendar.readonly',
|
||||
'Home-Assistant.io',
|
||||
)
|
||||
|
||||
persistent_notification = loader.get_component('persistent_notification')
|
||||
try:
|
||||
dev_flow = oauth.step1_get_device_and_user_codes()
|
||||
except OAuth2DeviceCodeError as err:
|
||||
persistent_notification.create(
|
||||
hass, 'Error: {}<br />You will need to restart hass after fixing.'
|
||||
''.format(err),
|
||||
title=NOTIFICATION_TITLE,
|
||||
notification_id=NOTIFICATION_ID)
|
||||
return False
|
||||
|
||||
persistent_notification.create(
|
||||
hass, 'In order to authorize Home-Assistant to view your calendars'
|
||||
'You must visit: <a href="{}" target="_blank">{}</a> and enter'
|
||||
'code: {}'.format(dev_flow.verification_url,
|
||||
dev_flow.verification_url,
|
||||
dev_flow.user_code),
|
||||
title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID
|
||||
)
|
||||
|
||||
def step2_exchange(now):
|
||||
"""Keep trying to validate the user_code until it expires."""
|
||||
if now >= dt.as_local(dev_flow.user_code_expiry):
|
||||
persistent_notification.create(
|
||||
hass, 'Authenication code expired, please restart '
|
||||
'Home-Assistant and try again',
|
||||
title=NOTIFICATION_TITLE,
|
||||
notification_id=NOTIFICATION_ID)
|
||||
listener()
|
||||
|
||||
try:
|
||||
credentials = oauth.step2_exchange(device_flow_info=dev_flow)
|
||||
except FlowExchangeError:
|
||||
# not ready yet, call again
|
||||
return
|
||||
|
||||
storage = Storage(hass.config.path(TOKEN_FILE))
|
||||
storage.put(credentials)
|
||||
do_setup(hass, config)
|
||||
listener()
|
||||
persistent_notification.create(
|
||||
hass, 'We are all setup now. Check {} for calendars that have '
|
||||
'been found'.format(YAML_DEVICES),
|
||||
title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID)
|
||||
|
||||
listener = track_time_change(hass, step2_exchange,
|
||||
second=range(0, 60, dev_flow.interval))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup the platform."""
|
||||
if DATA_INDEX not in hass.data:
|
||||
hass.data[DATA_INDEX] = {}
|
||||
|
||||
conf = config.get(DOMAIN, {})
|
||||
|
||||
token_file = hass.config.path(TOKEN_FILE)
|
||||
if not os.path.isfile(token_file):
|
||||
do_authentication(hass, conf)
|
||||
else:
|
||||
do_setup(hass, conf)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def setup_services(hass, track_new_found_calendars, calendar_service):
|
||||
"""Setup service listeners."""
|
||||
def _found_calendar(call):
|
||||
"""Check if we know about a calendar and generate PLATFORM_DISCOVER."""
|
||||
calendar = get_calendar_info(hass, call.data)
|
||||
if hass.data[DATA_INDEX].get(calendar[CONF_CAL_ID], None) is not None:
|
||||
return
|
||||
|
||||
hass.data[DATA_INDEX].update({calendar[CONF_CAL_ID]: calendar})
|
||||
|
||||
update_config(
|
||||
hass.config.path(YAML_DEVICES),
|
||||
hass.data[DATA_INDEX][calendar[CONF_CAL_ID]]
|
||||
)
|
||||
|
||||
discovery.load_platform(hass, 'calendar', DOMAIN,
|
||||
hass.data[DATA_INDEX][calendar[CONF_CAL_ID]])
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN, SERVICE_FOUND_CALENDARS, _found_calendar,
|
||||
None, schema=None)
|
||||
|
||||
def _scan_for_calendars(service):
|
||||
"""Scan for new calendars."""
|
||||
service = calendar_service.get()
|
||||
cal_list = service.calendarList() # pylint: disable=no-member
|
||||
calendars = cal_list.list().execute()['items']
|
||||
for calendar in calendars:
|
||||
calendar['track'] = track_new_found_calendars
|
||||
hass.services.call(DOMAIN, SERVICE_FOUND_CALENDARS,
|
||||
calendar)
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN, SERVICE_SCAN_CALENDARS,
|
||||
_scan_for_calendars,
|
||||
None, schema=None)
|
||||
return True
|
||||
|
||||
|
||||
def do_setup(hass, config):
|
||||
"""Run the setup after we have everything configured."""
|
||||
# load calendars the user has configured
|
||||
hass.data[DATA_INDEX] = load_config(hass.config.path(YAML_DEVICES))
|
||||
|
||||
calendar_service = GoogleCalendarService(hass.config.path(TOKEN_FILE))
|
||||
track_new_found_calendars = convert(config.get(CONF_TRACK_NEW),
|
||||
bool, DEFAULT_CONF_TRACK_NEW)
|
||||
setup_services(hass, track_new_found_calendars, calendar_service)
|
||||
|
||||
# Ensure component is loaded
|
||||
bootstrap.setup_component(hass, 'calendar', config)
|
||||
|
||||
for calendar in hass.data[DATA_INDEX].values():
|
||||
discovery.load_platform(hass, 'calendar', DOMAIN, calendar)
|
||||
|
||||
# look for any new calendars
|
||||
hass.services.call(DOMAIN, SERVICE_SCAN_CALENDARS, None)
|
||||
return True
|
||||
|
||||
|
||||
class GoogleCalendarService(object):
|
||||
"""Calendar service interface to google."""
|
||||
|
||||
def __init__(self, token_file):
|
||||
"""We just need the token_file."""
|
||||
self.token_file = token_file
|
||||
|
||||
def get(self):
|
||||
"""Get the calendar service from the storage file token."""
|
||||
import httplib2
|
||||
from oauth2client.file import Storage
|
||||
from googleapiclient import discovery as google_discovery
|
||||
credentials = Storage(self.token_file).get()
|
||||
http = credentials.authorize(httplib2.Http())
|
||||
service = google_discovery.build('calendar', 'v3', http=http)
|
||||
return service
|
||||
|
||||
|
||||
def get_calendar_info(hass, calendar):
|
||||
"""Convert data from Google into DEVICE_SCHEMA."""
|
||||
calendar_info = DEVICE_SCHEMA({
|
||||
CONF_CAL_ID: calendar['id'],
|
||||
CONF_ENTITIES: [{
|
||||
CONF_TRACK: calendar['track'],
|
||||
CONF_NAME: calendar['summary'],
|
||||
CONF_DEVICE_ID: generate_entity_id('{}', calendar['summary'],
|
||||
hass=hass),
|
||||
}]
|
||||
})
|
||||
return calendar_info
|
||||
|
||||
|
||||
def load_config(path):
|
||||
"""Load the google_calendar_devices.yaml."""
|
||||
calendars = {}
|
||||
try:
|
||||
with open(path) as file:
|
||||
data = yaml.load(file)
|
||||
for calendar in data:
|
||||
try:
|
||||
calendars.update({calendar[CONF_CAL_ID]:
|
||||
DEVICE_SCHEMA(calendar)})
|
||||
except VoluptuousError as exception:
|
||||
# keep going
|
||||
_LOGGER.warning('Calendar Invalid Data: %s', exception)
|
||||
except FileNotFoundError:
|
||||
# When YAML file could not be loaded/did not contain a dict
|
||||
return {}
|
||||
|
||||
return calendars
|
||||
|
||||
|
||||
def update_config(path, calendar):
|
||||
"""Write the google_calendar_devices.yaml."""
|
||||
with open(path, 'a') as out:
|
||||
out.write('\n')
|
||||
yaml.dump([calendar], out, default_flow_style=False)
|
|
@ -124,6 +124,9 @@ fuzzywuzzy==0.14.0
|
|||
# homeassistant.components.notify.gntp
|
||||
gntp==1.0.3
|
||||
|
||||
# homeassistant.components.google
|
||||
google-api-python-client==1.5.5
|
||||
|
||||
# homeassistant.components.sensor.google_travel_time
|
||||
googlemaps==2.4.4
|
||||
|
||||
|
@ -280,6 +283,9 @@ netdisco==0.7.6
|
|||
# homeassistant.components.sensor.neurio_energy
|
||||
neurio==0.2.10
|
||||
|
||||
# homeassistant.components.google
|
||||
oauth2client==3.0.0
|
||||
|
||||
# homeassistant.components.switch.orvibo
|
||||
orvibo==1.1.1
|
||||
|
||||
|
|
1
tests/components/calendar/__init__.py
Normal file
1
tests/components/calendar/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""The tests for calendar sensor platforms."""
|
421
tests/components/calendar/test_google.py
Normal file
421
tests/components/calendar/test_google.py
Normal file
|
@ -0,0 +1,421 @@
|
|||
"""The tests for the google calendar component."""
|
||||
# pylint: disable=protected-access
|
||||
import logging
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
import homeassistant.components.calendar as calendar_base
|
||||
import homeassistant.components.calendar.google as calendar
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.const import CONF_PLATFORM, STATE_OFF, STATE_ON
|
||||
from homeassistant.helpers.template import DATE_STR_FORMAT
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
TEST_PLATFORM = {calendar_base.DOMAIN: {CONF_PLATFORM: 'test'}}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestComponentsGoogleCalendar(unittest.TestCase):
|
||||
"""Test the Google calendar."""
|
||||
|
||||
hass = None # HomeAssistant
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
# Set our timezone to CST/Regina so we can check calculations
|
||||
# This keeps UTC-6 all year round
|
||||
dt_util.set_default_time_zone(dt_util.get_time_zone('America/Regina'))
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
dt_util.set_default_time_zone(dt_util.get_time_zone('UTC'))
|
||||
|
||||
self.hass.stop()
|
||||
|
||||
@patch('homeassistant.components.calendar.google.GoogleCalendarData')
|
||||
def test_all_day_event(self, mock_next_event):
|
||||
"""Test that we can create an event trigger on device."""
|
||||
week_from_today = dt_util.dt.date.today() \
|
||||
+ dt_util.dt.timedelta(days=7)
|
||||
event = {
|
||||
'summary': 'Test All Day Event',
|
||||
'start': {
|
||||
'date': week_from_today.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'date': (week_from_today + dt_util.dt.timedelta(days=1))
|
||||
.isoformat()
|
||||
},
|
||||
'location': 'Test Cases',
|
||||
'description': 'We\'re just testing that all day events get setup '
|
||||
'correctly',
|
||||
'kind': 'calendar#event',
|
||||
'created': '2016-06-23T16:37:57.000Z',
|
||||
'transparency': 'transparent',
|
||||
'updated': '2016-06-24T01:57:21.045Z',
|
||||
'reminders': {'useDefault': True},
|
||||
'organizer': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True
|
||||
},
|
||||
'sequence': 0,
|
||||
'creator': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True
|
||||
},
|
||||
'id': '_c8rinwq863h45qnucyoi43ny8',
|
||||
'etag': '"2933466882090000"',
|
||||
'htmlLink': 'https://www.google.com/calendar/event?eid=*******',
|
||||
'iCalUID': 'cydrevtfuybguinhomj@google.com',
|
||||
'status': 'confirmed'
|
||||
}
|
||||
|
||||
mock_next_event.return_value.event = event
|
||||
|
||||
device_name = 'Test All Day'
|
||||
|
||||
cal = calendar.GoogleCalendarEventDevice(self.hass, None,
|
||||
'', {'name': device_name})
|
||||
|
||||
self.assertEquals(cal.name, device_name)
|
||||
|
||||
self.assertEquals(cal.state, STATE_OFF)
|
||||
|
||||
self.assertFalse(cal.offset_reached())
|
||||
|
||||
self.assertEquals(cal.device_state_attributes, {
|
||||
'message': event['summary'],
|
||||
'all_day': True,
|
||||
'offset_reached': False,
|
||||
'start_time': '{} 06:00:00'.format(event['start']['date']),
|
||||
'end_time': '{} 06:00:00'.format(event['end']['date']),
|
||||
'location': event['location'],
|
||||
'description': event['description']
|
||||
})
|
||||
|
||||
@patch('homeassistant.components.calendar.google.GoogleCalendarData')
|
||||
def test_future_event(self, mock_next_event):
|
||||
"""Test that we can create an event trigger on device."""
|
||||
one_hour_from_now = dt_util.now() \
|
||||
+ dt_util.dt.timedelta(minutes=30)
|
||||
event = {
|
||||
'start': {
|
||||
'dateTime': one_hour_from_now.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'dateTime': (one_hour_from_now
|
||||
+ dt_util.dt.timedelta(minutes=60))
|
||||
.isoformat()
|
||||
},
|
||||
'summary': 'Test Event in 30 minutes',
|
||||
'reminders': {'useDefault': True},
|
||||
'id': 'aioehgni435lihje',
|
||||
'status': 'confirmed',
|
||||
'updated': '2016-11-05T15:52:07.329Z',
|
||||
'organizer': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True,
|
||||
},
|
||||
'created': '2016-11-05T15:52:07.000Z',
|
||||
'iCalUID': 'dsfohuygtfvgbhnuju@google.com',
|
||||
'sequence': 0,
|
||||
'creator': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
},
|
||||
'etag': '"2956722254658000"',
|
||||
'kind': 'calendar#event',
|
||||
'htmlLink': 'https://www.google.com/calendar/event?eid=*******',
|
||||
}
|
||||
mock_next_event.return_value.event = event
|
||||
|
||||
device_name = 'Test Future Event'
|
||||
device_id = 'test_future_event'
|
||||
|
||||
cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id,
|
||||
{'name': device_name})
|
||||
|
||||
self.assertEquals(cal.name, device_name)
|
||||
|
||||
self.assertEquals(cal.state, STATE_OFF)
|
||||
|
||||
self.assertFalse(cal.offset_reached())
|
||||
|
||||
self.assertEquals(cal.device_state_attributes, {
|
||||
'message': event['summary'],
|
||||
'all_day': False,
|
||||
'offset_reached': False,
|
||||
'start_time': one_hour_from_now.strftime(DATE_STR_FORMAT),
|
||||
'end_time':
|
||||
(one_hour_from_now + dt_util.dt.timedelta(minutes=60))
|
||||
.strftime(DATE_STR_FORMAT),
|
||||
'location': '',
|
||||
'description': ''
|
||||
})
|
||||
|
||||
@patch('homeassistant.components.calendar.google.GoogleCalendarData')
|
||||
def test_in_progress_event(self, mock_next_event):
|
||||
"""Test that we can create an event trigger on device."""
|
||||
middle_of_event = dt_util.now() \
|
||||
- dt_util.dt.timedelta(minutes=30)
|
||||
event = {
|
||||
'start': {
|
||||
'dateTime': middle_of_event.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'dateTime': (middle_of_event + dt_util.dt
|
||||
.timedelta(minutes=60))
|
||||
.isoformat()
|
||||
},
|
||||
'summary': 'Test Event in Progress',
|
||||
'reminders': {'useDefault': True},
|
||||
'id': 'aioehgni435lihje',
|
||||
'status': 'confirmed',
|
||||
'updated': '2016-11-05T15:52:07.329Z',
|
||||
'organizer': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True,
|
||||
},
|
||||
'created': '2016-11-05T15:52:07.000Z',
|
||||
'iCalUID': 'dsfohuygtfvgbhnuju@google.com',
|
||||
'sequence': 0,
|
||||
'creator': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
},
|
||||
'etag': '"2956722254658000"',
|
||||
'kind': 'calendar#event',
|
||||
'htmlLink': 'https://www.google.com/calendar/event?eid=*******',
|
||||
}
|
||||
|
||||
mock_next_event.return_value.event = event
|
||||
|
||||
device_name = 'Test Event in Progress'
|
||||
device_id = 'test_event_in_progress'
|
||||
|
||||
cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id,
|
||||
{'name': device_name})
|
||||
|
||||
self.assertEquals(cal.name, device_name)
|
||||
|
||||
self.assertEquals(cal.state, STATE_ON)
|
||||
|
||||
self.assertFalse(cal.offset_reached())
|
||||
|
||||
self.assertEquals(cal.device_state_attributes, {
|
||||
'message': event['summary'],
|
||||
'all_day': False,
|
||||
'offset_reached': False,
|
||||
'start_time': middle_of_event.strftime(DATE_STR_FORMAT),
|
||||
'end_time':
|
||||
(middle_of_event + dt_util.dt.timedelta(minutes=60))
|
||||
.strftime(DATE_STR_FORMAT),
|
||||
'location': '',
|
||||
'description': ''
|
||||
})
|
||||
|
||||
@patch('homeassistant.components.calendar.google.GoogleCalendarData')
|
||||
def test_offset_in_progress_event(self, mock_next_event):
|
||||
"""Test that we can create an event trigger on device."""
|
||||
middle_of_event = dt_util.now() \
|
||||
+ dt_util.dt.timedelta(minutes=14)
|
||||
event_summary = 'Test Event in Progress'
|
||||
event = {
|
||||
'start': {
|
||||
'dateTime': middle_of_event.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'dateTime': (middle_of_event + dt_util.dt
|
||||
.timedelta(minutes=60))
|
||||
.isoformat()
|
||||
},
|
||||
'summary': '{} !!-15'.format(event_summary),
|
||||
'reminders': {'useDefault': True},
|
||||
'id': 'aioehgni435lihje',
|
||||
'status': 'confirmed',
|
||||
'updated': '2016-11-05T15:52:07.329Z',
|
||||
'organizer': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True,
|
||||
},
|
||||
'created': '2016-11-05T15:52:07.000Z',
|
||||
'iCalUID': 'dsfohuygtfvgbhnuju@google.com',
|
||||
'sequence': 0,
|
||||
'creator': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
},
|
||||
'etag': '"2956722254658000"',
|
||||
'kind': 'calendar#event',
|
||||
'htmlLink': 'https://www.google.com/calendar/event?eid=*******',
|
||||
}
|
||||
|
||||
mock_next_event.return_value.event = event
|
||||
|
||||
device_name = 'Test Event in Progress'
|
||||
device_id = 'test_event_in_progress'
|
||||
|
||||
cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id,
|
||||
{'name': device_name})
|
||||
|
||||
self.assertEquals(cal.name, device_name)
|
||||
|
||||
self.assertEquals(cal.state, STATE_OFF)
|
||||
|
||||
self.assertTrue(cal.offset_reached())
|
||||
|
||||
self.assertEquals(cal.device_state_attributes, {
|
||||
'message': event_summary,
|
||||
'all_day': False,
|
||||
'offset_reached': True,
|
||||
'start_time': middle_of_event.strftime(DATE_STR_FORMAT),
|
||||
'end_time':
|
||||
(middle_of_event + dt_util.dt.timedelta(minutes=60))
|
||||
.strftime(DATE_STR_FORMAT),
|
||||
'location': '',
|
||||
'description': ''
|
||||
})
|
||||
|
||||
@patch('homeassistant.components.calendar.google.GoogleCalendarData')
|
||||
def test_all_day_offset_in_progress_event(self, mock_next_event):
|
||||
"""Test that we can create an event trigger on device."""
|
||||
tomorrow = dt_util.dt.date.today() \
|
||||
+ dt_util.dt.timedelta(days=1)
|
||||
|
||||
offset_hours = (25 - dt_util.now().hour)
|
||||
event_summary = 'Test All Day Event Offset In Progress'
|
||||
event = {
|
||||
'summary': '{} !!-{}:0'.format(event_summary, offset_hours),
|
||||
'start': {
|
||||
'date': tomorrow.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'date': (tomorrow + dt_util.dt.timedelta(days=1))
|
||||
.isoformat()
|
||||
},
|
||||
'location': 'Test Cases',
|
||||
'description': 'We\'re just testing that all day events get setup '
|
||||
'correctly',
|
||||
'kind': 'calendar#event',
|
||||
'created': '2016-06-23T16:37:57.000Z',
|
||||
'transparency': 'transparent',
|
||||
'updated': '2016-06-24T01:57:21.045Z',
|
||||
'reminders': {'useDefault': True},
|
||||
'organizer': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True
|
||||
},
|
||||
'sequence': 0,
|
||||
'creator': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True
|
||||
},
|
||||
'id': '_c8rinwq863h45qnucyoi43ny8',
|
||||
'etag': '"2933466882090000"',
|
||||
'htmlLink': 'https://www.google.com/calendar/event?eid=*******',
|
||||
'iCalUID': 'cydrevtfuybguinhomj@google.com',
|
||||
'status': 'confirmed'
|
||||
}
|
||||
|
||||
mock_next_event.return_value.event = event
|
||||
|
||||
device_name = 'Test All Day Offset In Progress'
|
||||
device_id = 'test_all_day_offset_in_progress'
|
||||
|
||||
cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id,
|
||||
{'name': device_name})
|
||||
|
||||
self.assertEquals(cal.name, device_name)
|
||||
|
||||
self.assertEquals(cal.state, STATE_OFF)
|
||||
|
||||
self.assertTrue(cal.offset_reached())
|
||||
|
||||
self.assertEquals(cal.device_state_attributes, {
|
||||
'message': event_summary,
|
||||
'all_day': True,
|
||||
'offset_reached': True,
|
||||
'start_time': '{} 06:00:00'.format(event['start']['date']),
|
||||
'end_time': '{} 06:00:00'.format(event['end']['date']),
|
||||
'location': event['location'],
|
||||
'description': event['description']
|
||||
})
|
||||
|
||||
@patch('homeassistant.components.calendar.google.GoogleCalendarData')
|
||||
def test_all_day_offset_event(self, mock_next_event):
|
||||
"""Test that we can create an event trigger on device."""
|
||||
tomorrow = dt_util.dt.date.today() \
|
||||
+ dt_util.dt.timedelta(days=2)
|
||||
|
||||
offset_hours = (1 + dt_util.now().hour)
|
||||
event_summary = 'Test All Day Event Offset'
|
||||
event = {
|
||||
'summary': '{} !!-{}:0'.format(event_summary, offset_hours),
|
||||
'start': {
|
||||
'date': tomorrow.isoformat()
|
||||
},
|
||||
'end': {
|
||||
'date': (tomorrow + dt_util.dt.timedelta(days=1))
|
||||
.isoformat()
|
||||
},
|
||||
'location': 'Test Cases',
|
||||
'description': 'We\'re just testing that all day events get setup '
|
||||
'correctly',
|
||||
'kind': 'calendar#event',
|
||||
'created': '2016-06-23T16:37:57.000Z',
|
||||
'transparency': 'transparent',
|
||||
'updated': '2016-06-24T01:57:21.045Z',
|
||||
'reminders': {'useDefault': True},
|
||||
'organizer': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True
|
||||
},
|
||||
'sequence': 0,
|
||||
'creator': {
|
||||
'email': 'uvrttabwegnui4gtia3vyqb@import.calendar.google.com',
|
||||
'displayName': 'Organizer Name',
|
||||
'self': True
|
||||
},
|
||||
'id': '_c8rinwq863h45qnucyoi43ny8',
|
||||
'etag': '"2933466882090000"',
|
||||
'htmlLink': 'https://www.google.com/calendar/event?eid=*******',
|
||||
'iCalUID': 'cydrevtfuybguinhomj@google.com',
|
||||
'status': 'confirmed'
|
||||
}
|
||||
|
||||
mock_next_event.return_value.event = event
|
||||
|
||||
device_name = 'Test All Day Offset'
|
||||
device_id = 'test_all_day_offset'
|
||||
|
||||
cal = calendar.GoogleCalendarEventDevice(self.hass, None, device_id,
|
||||
{'name': device_name})
|
||||
|
||||
self.assertEquals(cal.name, device_name)
|
||||
|
||||
self.assertEquals(cal.state, STATE_OFF)
|
||||
|
||||
self.assertFalse(cal.offset_reached())
|
||||
|
||||
self.assertEquals(cal.device_state_attributes, {
|
||||
'message': event_summary,
|
||||
'all_day': True,
|
||||
'offset_reached': False,
|
||||
'start_time': '{} 06:00:00'.format(event['start']['date']),
|
||||
'end_time': '{} 06:00:00'.format(event['end']['date']),
|
||||
'location': event['location'],
|
||||
'description': event['description']
|
||||
})
|
1
tests/components/calendar/test_init.py
Normal file
1
tests/components/calendar/test_init.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""The tests for the calendar component."""
|
90
tests/components/test_google.py
Normal file
90
tests/components/test_google.py
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""The tests for the Google Calendar component."""
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
import homeassistant.components.google as google
|
||||
from homeassistant.bootstrap import setup_component
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestGoogle(unittest.TestCase):
|
||||
"""Test the Google component."""
|
||||
|
||||
def setUp(self): # pylint: disable=invalid-name
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_setup_component(self):
|
||||
"""Test setup component."""
|
||||
config = {
|
||||
'google': {
|
||||
'client_id': 'id',
|
||||
'client_secret': 'secret',
|
||||
}
|
||||
}
|
||||
|
||||
self.assertTrue(setup_component(self.hass, 'google', config))
|
||||
|
||||
def test_get_calendar_info(self):
|
||||
calendar = {
|
||||
'id': 'qwertyuiopasdfghjklzxcvbnm@import.calendar.google.com',
|
||||
'etag': '"3584134138943410"',
|
||||
'timeZone': 'UTC',
|
||||
'accessRole': 'reader',
|
||||
'foregroundColor': '#000000',
|
||||
'selected': True,
|
||||
'kind': 'calendar#calendarListEntry',
|
||||
'backgroundColor': '#16a765',
|
||||
'description': 'Test Calendar',
|
||||
'summary': 'We are, we are, a... Test Calendar',
|
||||
'colorId': '8',
|
||||
'defaultReminders': [],
|
||||
'track': True
|
||||
}
|
||||
|
||||
calendar_info = google.get_calendar_info(self.hass, calendar)
|
||||
self.assertEquals(calendar_info, {
|
||||
'cal_id': 'qwertyuiopasdfghjklzxcvbnm@import.calendar.google.com',
|
||||
'entities': [{
|
||||
'device_id': 'we_are_we_are_a_test_calendar',
|
||||
'name': 'We are, we are, a... Test Calendar',
|
||||
'track': True,
|
||||
}]
|
||||
})
|
||||
|
||||
def test_found_calendar(self):
|
||||
calendar = {
|
||||
'id': 'qwertyuiopasdfghjklzxcvbnm@import.calendar.google.com',
|
||||
'etag': '"3584134138943410"',
|
||||
'timeZone': 'UTC',
|
||||
'accessRole': 'reader',
|
||||
'foregroundColor': '#000000',
|
||||
'selected': True,
|
||||
'kind': 'calendar#calendarListEntry',
|
||||
'backgroundColor': '#16a765',
|
||||
'description': 'Test Calendar',
|
||||
'summary': 'We are, we are, a... Test Calendar',
|
||||
'colorId': '8',
|
||||
'defaultReminders': [],
|
||||
'track': True
|
||||
}
|
||||
|
||||
# self.assertIsInstance(self.hass.data[google.DATA_INDEX], dict)
|
||||
# self.assertEquals(self.hass.data[google.DATA_INDEX], {})
|
||||
|
||||
calendar_service = google.GoogleCalendarService(
|
||||
self.hass.config.path(google.TOKEN_FILE))
|
||||
self.assertTrue(google.setup_services(self.hass, True,
|
||||
calendar_service))
|
||||
self.hass.services.call('google', 'found_calendar', calendar,
|
||||
blocking=True)
|
||||
|
||||
# TODO: Fix this
|
||||
# self.assertTrue(self.hass.data[google.DATA_INDEX]
|
||||
# # .get(calendar['id'], None) is not None)
|
Loading…
Add table
Reference in a new issue