mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Remember the Milk - updating and completing tasks (#11069)
* Remember the Milk - updating and completing tasks Added new feature so that tasks can be updated and completed. For this feature a task id must be set when creating the task. * fixed hould complaints * fixed review comments by @MartinHjelmare * removed unnecessary check as proposed by @MartinHjelmare
This commit is contained in:
parent
3fd620198e
commit
7759ab6919
@ -15,7 +15,8 @@ import json
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.const import (CONF_API_KEY, STATE_OK, CONF_TOKEN, CONF_NAME)
|
from homeassistant.const import (CONF_API_KEY, STATE_OK, CONF_TOKEN,
|
||||||
|
CONF_NAME, CONF_ID)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
@ -31,6 +32,10 @@ DEFAULT_NAME = DOMAIN
|
|||||||
GROUP_NAME_RTM = 'remember the milk accounts'
|
GROUP_NAME_RTM = 'remember the milk accounts'
|
||||||
|
|
||||||
CONF_SHARED_SECRET = 'shared_secret'
|
CONF_SHARED_SECRET = 'shared_secret'
|
||||||
|
CONF_ID_MAP = 'id_map'
|
||||||
|
CONF_LIST_ID = 'list_id'
|
||||||
|
CONF_TIMESERIES_ID = 'timeseries_id'
|
||||||
|
CONF_TASK_ID = 'task_id'
|
||||||
|
|
||||||
RTM_SCHEMA = vol.Schema({
|
RTM_SCHEMA = vol.Schema({
|
||||||
vol.Required(CONF_NAME): cv.string,
|
vol.Required(CONF_NAME): cv.string,
|
||||||
@ -44,9 +49,15 @@ CONFIG_SCHEMA = vol.Schema({
|
|||||||
|
|
||||||
CONFIG_FILE_NAME = '.remember_the_milk.conf'
|
CONFIG_FILE_NAME = '.remember_the_milk.conf'
|
||||||
SERVICE_CREATE_TASK = 'create_task'
|
SERVICE_CREATE_TASK = 'create_task'
|
||||||
|
SERVICE_COMPLETE_TASK = 'complete_task'
|
||||||
|
|
||||||
SERVICE_SCHEMA_CREATE_TASK = vol.Schema({
|
SERVICE_SCHEMA_CREATE_TASK = vol.Schema({
|
||||||
vol.Required(CONF_NAME): cv.string,
|
vol.Required(CONF_NAME): cv.string,
|
||||||
|
vol.Optional(CONF_ID): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
SERVICE_SCHEMA_COMPLETE_TASK = vol.Schema({
|
||||||
|
vol.Required(CONF_ID): cv.string,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -84,10 +95,14 @@ def _create_instance(hass, account_name, api_key, shared_secret,
|
|||||||
entity = RememberTheMilk(account_name, api_key, shared_secret,
|
entity = RememberTheMilk(account_name, api_key, shared_secret,
|
||||||
token, stored_rtm_config)
|
token, stored_rtm_config)
|
||||||
component.add_entity(entity)
|
component.add_entity(entity)
|
||||||
hass.services.async_register(
|
hass.services.register(
|
||||||
DOMAIN, '{}_create_task'.format(account_name), entity.create_task,
|
DOMAIN, '{}_create_task'.format(account_name), entity.create_task,
|
||||||
description=descriptions.get(SERVICE_CREATE_TASK),
|
description=descriptions.get(SERVICE_CREATE_TASK),
|
||||||
schema=SERVICE_SCHEMA_CREATE_TASK)
|
schema=SERVICE_SCHEMA_CREATE_TASK)
|
||||||
|
hass.services.register(
|
||||||
|
DOMAIN, '{}_complete_task'.format(account_name), entity.complete_task,
|
||||||
|
description=descriptions.get(SERVICE_COMPLETE_TASK),
|
||||||
|
schema=SERVICE_SCHEMA_COMPLETE_TASK)
|
||||||
|
|
||||||
|
|
||||||
def _register_new_account(hass, account_name, api_key, shared_secret,
|
def _register_new_account(hass, account_name, api_key, shared_secret,
|
||||||
@ -168,8 +183,7 @@ class RememberTheMilkConfiguration(object):
|
|||||||
|
|
||||||
def set_token(self, profile_name, token):
|
def set_token(self, profile_name, token):
|
||||||
"""Store a new server token for a profile."""
|
"""Store a new server token for a profile."""
|
||||||
if profile_name not in self._config:
|
self._initialize_profile(profile_name)
|
||||||
self._config[profile_name] = dict()
|
|
||||||
self._config[profile_name][CONF_TOKEN] = token
|
self._config[profile_name][CONF_TOKEN] = token
|
||||||
self.save_config()
|
self.save_config()
|
||||||
|
|
||||||
@ -181,6 +195,44 @@ class RememberTheMilkConfiguration(object):
|
|||||||
self._config.pop(profile_name, None)
|
self._config.pop(profile_name, None)
|
||||||
self.save_config()
|
self.save_config()
|
||||||
|
|
||||||
|
def _initialize_profile(self, profile_name):
|
||||||
|
"""Initialize the data structures for a profile."""
|
||||||
|
if profile_name not in self._config:
|
||||||
|
self._config[profile_name] = dict()
|
||||||
|
if CONF_ID_MAP not in self._config[profile_name]:
|
||||||
|
self._config[profile_name][CONF_ID_MAP] = dict()
|
||||||
|
|
||||||
|
def get_rtm_id(self, profile_name, hass_id):
|
||||||
|
"""Get the rtm ids for a home assistant task id.
|
||||||
|
|
||||||
|
The id of a rtm tasks consists of the tuple:
|
||||||
|
list id, timeseries id and the task id.
|
||||||
|
"""
|
||||||
|
self._initialize_profile(profile_name)
|
||||||
|
ids = self._config[profile_name][CONF_ID_MAP].get(hass_id)
|
||||||
|
if ids is None:
|
||||||
|
return None
|
||||||
|
return ids[CONF_LIST_ID], ids[CONF_TIMESERIES_ID], ids[CONF_TASK_ID]
|
||||||
|
|
||||||
|
def set_rtm_id(self, profile_name, hass_id, list_id, time_series_id,
|
||||||
|
rtm_task_id):
|
||||||
|
"""Add/Update the rtm task id for a home assistant task id."""
|
||||||
|
self._initialize_profile(profile_name)
|
||||||
|
id_tuple = {
|
||||||
|
CONF_LIST_ID: list_id,
|
||||||
|
CONF_TIMESERIES_ID: time_series_id,
|
||||||
|
CONF_TASK_ID: rtm_task_id,
|
||||||
|
}
|
||||||
|
self._config[profile_name][CONF_ID_MAP][hass_id] = id_tuple
|
||||||
|
self.save_config()
|
||||||
|
|
||||||
|
def delete_rtm_id(self, profile_name, hass_id):
|
||||||
|
"""Delete a key mapping."""
|
||||||
|
self._initialize_profile(profile_name)
|
||||||
|
if hass_id in self._config[profile_name][CONF_ID_MAP]:
|
||||||
|
del self._config[profile_name][CONF_ID_MAP][hass_id]
|
||||||
|
self.save_config()
|
||||||
|
|
||||||
|
|
||||||
class RememberTheMilk(Entity):
|
class RememberTheMilk(Entity):
|
||||||
"""MVP implementation of an interface to Remember The Milk."""
|
"""MVP implementation of an interface to Remember The Milk."""
|
||||||
@ -225,19 +277,65 @@ class RememberTheMilk(Entity):
|
|||||||
import rtmapi
|
import rtmapi
|
||||||
|
|
||||||
try:
|
try:
|
||||||
task_name = call.data.get('name')
|
task_name = call.data.get(CONF_NAME)
|
||||||
|
hass_id = call.data.get(CONF_ID)
|
||||||
|
rtm_id = None
|
||||||
|
if hass_id is not None:
|
||||||
|
rtm_id = self._rtm_config.get_rtm_id(self._name, hass_id)
|
||||||
result = self._rtm_api.rtm.timelines.create()
|
result = self._rtm_api.rtm.timelines.create()
|
||||||
timeline = result.timeline.value
|
timeline = result.timeline.value
|
||||||
self._rtm_api.rtm.tasks.add(
|
|
||||||
timeline=timeline, name=task_name, parse='1')
|
if hass_id is None or rtm_id is None:
|
||||||
_LOGGER.debug('created new task "%s" in account %s',
|
result = self._rtm_api.rtm.tasks.add(
|
||||||
task_name, self.name)
|
timeline=timeline, name=task_name, parse='1')
|
||||||
|
_LOGGER.debug('created new task "%s" in account %s',
|
||||||
|
task_name, self.name)
|
||||||
|
self._rtm_config.set_rtm_id(self._name,
|
||||||
|
hass_id,
|
||||||
|
result.list.id,
|
||||||
|
result.list.taskseries.id,
|
||||||
|
result.list.taskseries.task.id)
|
||||||
|
else:
|
||||||
|
self._rtm_api.rtm.tasks.setName(name=task_name,
|
||||||
|
list_id=rtm_id[0],
|
||||||
|
taskseries_id=rtm_id[1],
|
||||||
|
task_id=rtm_id[2],
|
||||||
|
timeline=timeline)
|
||||||
|
_LOGGER.debug('updated task with id "%s" in account '
|
||||||
|
'%s to name %s',
|
||||||
|
hass_id, self.name, task_name)
|
||||||
except rtmapi.RtmRequestFailedException as rtm_exception:
|
except rtmapi.RtmRequestFailedException as rtm_exception:
|
||||||
_LOGGER.error('Error creating new Remember The Milk task for '
|
_LOGGER.error('Error creating new Remember The Milk task for '
|
||||||
'account %s: %s', self._name, rtm_exception)
|
'account %s: %s', self._name, rtm_exception)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def complete_task(self, call):
|
||||||
|
"""Complete a task that was previously created by this component."""
|
||||||
|
import rtmapi
|
||||||
|
|
||||||
|
hass_id = call.data.get(CONF_ID)
|
||||||
|
rtm_id = self._rtm_config.get_rtm_id(self._name, hass_id)
|
||||||
|
if rtm_id is None:
|
||||||
|
_LOGGER.error('Could not find task with id %s in account %s. '
|
||||||
|
'So task could not be closed.',
|
||||||
|
hass_id, self._name)
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
result = self._rtm_api.rtm.timelines.create()
|
||||||
|
timeline = result.timeline.value
|
||||||
|
self._rtm_api.rtm.tasks.complete(list_id=rtm_id[0],
|
||||||
|
taskseries_id=rtm_id[1],
|
||||||
|
task_id=rtm_id[2],
|
||||||
|
timeline=timeline)
|
||||||
|
self._rtm_config.delete_rtm_id(self._name, hass_id)
|
||||||
|
_LOGGER.debug('Completed task with id %s in account %s',
|
||||||
|
hass_id, self._name)
|
||||||
|
except rtmapi.RtmRequestFailedException as rtm_exception:
|
||||||
|
_LOGGER.error('Error creating new Remember The Milk task for '
|
||||||
|
'account %s: %s', self._name, rtm_exception)
|
||||||
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the device."""
|
"""Return the name of the device."""
|
||||||
|
@ -1,9 +1,24 @@
|
|||||||
# Describes the format for available Remember The Milk services
|
# Describes the format for available Remember The Milk services
|
||||||
|
|
||||||
create_task:
|
create_task:
|
||||||
description: Create a new task in your Remember The Milk account
|
description: >
|
||||||
|
Create (or update) a new task in your Remember The Milk account. If you want to update a task
|
||||||
|
later on, you have to set an "id" when creating the task.
|
||||||
|
Note: Updating a tasks does not support the smart syntax.
|
||||||
|
|
||||||
fields:
|
fields:
|
||||||
name:
|
name:
|
||||||
description: name of the new task, you can use the smart syntax here
|
description: name of the new task, you can use the smart syntax here
|
||||||
example: 'do this ^today #from_hass'
|
example: 'do this ^today #from_hass'
|
||||||
|
|
||||||
|
id:
|
||||||
|
description: (optional) identifier for the task you're creating, can be used to update or complete the task later on
|
||||||
|
example: myid
|
||||||
|
|
||||||
|
complete_task:
|
||||||
|
description: Complete a tasks that was privously created.
|
||||||
|
|
||||||
|
fields:
|
||||||
|
id:
|
||||||
|
description: identifier that was defined when creating the task
|
||||||
|
example: myid
|
@ -1,6 +1,7 @@
|
|||||||
"""Tests for the Remember The Milk component."""
|
"""Tests for the Remember The Milk component."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import json
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch, mock_open, Mock
|
from unittest.mock import patch, mock_open, Mock
|
||||||
|
|
||||||
@ -19,7 +20,16 @@ class TestConfiguration(unittest.TestCase):
|
|||||||
self.hass = get_test_home_assistant()
|
self.hass = get_test_home_assistant()
|
||||||
self.profile = "myprofile"
|
self.profile = "myprofile"
|
||||||
self.token = "mytoken"
|
self.token = "mytoken"
|
||||||
self.json_string = '{"myprofile": {"token": "mytoken"}}'
|
self.json_string = json.dumps(
|
||||||
|
{"myprofile": {
|
||||||
|
"token": "mytoken",
|
||||||
|
"id_map": {"1234": {
|
||||||
|
"list_id": "0",
|
||||||
|
"timeseries_id": "1",
|
||||||
|
"task_id": "2"
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""Exit home assistant."""
|
"""Exit home assistant."""
|
||||||
@ -47,3 +57,29 @@ class TestConfiguration(unittest.TestCase):
|
|||||||
patch("os.path.isfile", Mock(return_value=True)):
|
patch("os.path.isfile", Mock(return_value=True)):
|
||||||
config = rtm.RememberTheMilkConfiguration(self.hass)
|
config = rtm.RememberTheMilkConfiguration(self.hass)
|
||||||
self.assertIsNotNone(config)
|
self.assertIsNotNone(config)
|
||||||
|
|
||||||
|
def test_id_map(self):
|
||||||
|
"""Test the hass to rtm task is mapping."""
|
||||||
|
hass_id = "hass-id-1234"
|
||||||
|
list_id = "mylist"
|
||||||
|
timeseries_id = "my_timeseries"
|
||||||
|
rtm_id = "rtm-id-4567"
|
||||||
|
with patch("builtins.open", mock_open()), \
|
||||||
|
patch("os.path.isfile", Mock(return_value=False)):
|
||||||
|
config = rtm.RememberTheMilkConfiguration(self.hass)
|
||||||
|
|
||||||
|
self.assertEqual(None, config.get_rtm_id(self.profile, hass_id))
|
||||||
|
config.set_rtm_id(self.profile, hass_id, list_id, timeseries_id,
|
||||||
|
rtm_id)
|
||||||
|
self.assertEqual((list_id, timeseries_id, rtm_id),
|
||||||
|
config.get_rtm_id(self.profile, hass_id))
|
||||||
|
config.delete_rtm_id(self.profile, hass_id)
|
||||||
|
self.assertEqual(None, config.get_rtm_id(self.profile, hass_id))
|
||||||
|
|
||||||
|
def test_load_key_map(self):
|
||||||
|
"""Test loading an existing key map from the file."""
|
||||||
|
with patch("builtins.open", mock_open(read_data=self.json_string)), \
|
||||||
|
patch("os.path.isfile", Mock(return_value=True)):
|
||||||
|
config = rtm.RememberTheMilkConfiguration(self.hass)
|
||||||
|
self.assertEqual(('0', '1', '2',),
|
||||||
|
config.get_rtm_id(self.profile, "1234"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user