Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support to interact with Remember The Milk."""
2 
3 import json
4 import logging
5 import os
6 
7 from rtmapi import Rtm
8 import voluptuous as vol
9 
10 from homeassistant.components import configurator
11 from homeassistant.const import CONF_API_KEY, CONF_ID, CONF_NAME, CONF_TOKEN
12 from homeassistant.core import HomeAssistant
14 from homeassistant.helpers.entity_component import EntityComponent
15 from homeassistant.helpers.typing import ConfigType
16 
17 from .entity import RememberTheMilkEntity
18 
19 # httplib2 is a transitive dependency from RtmAPI. If this dependency is not
20 # set explicitly, the library does not work.
21 _LOGGER = logging.getLogger(__name__)
22 
23 DOMAIN = "remember_the_milk"
24 DEFAULT_NAME = DOMAIN
25 
26 CONF_SHARED_SECRET = "shared_secret"
27 CONF_ID_MAP = "id_map"
28 CONF_LIST_ID = "list_id"
29 CONF_TIMESERIES_ID = "timeseries_id"
30 CONF_TASK_ID = "task_id"
31 
32 RTM_SCHEMA = vol.Schema(
33  {
34  vol.Required(CONF_NAME): cv.string,
35  vol.Required(CONF_API_KEY): cv.string,
36  vol.Required(CONF_SHARED_SECRET): cv.string,
37  }
38 )
39 
40 CONFIG_SCHEMA = vol.Schema(
41  {DOMAIN: vol.All(cv.ensure_list, [RTM_SCHEMA])}, extra=vol.ALLOW_EXTRA
42 )
43 
44 CONFIG_FILE_NAME = ".remember_the_milk.conf"
45 SERVICE_CREATE_TASK = "create_task"
46 SERVICE_COMPLETE_TASK = "complete_task"
47 
48 SERVICE_SCHEMA_CREATE_TASK = vol.Schema(
49  {vol.Required(CONF_NAME): cv.string, vol.Optional(CONF_ID): cv.string}
50 )
51 
52 SERVICE_SCHEMA_COMPLETE_TASK = vol.Schema({vol.Required(CONF_ID): cv.string})
53 
54 
55 def setup(hass: HomeAssistant, config: ConfigType) -> bool:
56  """Set up the Remember the milk component."""
57  component = EntityComponent[RememberTheMilkEntity](_LOGGER, DOMAIN, hass)
58 
59  stored_rtm_config = RememberTheMilkConfiguration(hass)
60  for rtm_config in config[DOMAIN]:
61  account_name = rtm_config[CONF_NAME]
62  _LOGGER.debug("Adding Remember the milk account %s", account_name)
63  api_key = rtm_config[CONF_API_KEY]
64  shared_secret = rtm_config[CONF_SHARED_SECRET]
65  token = stored_rtm_config.get_token(account_name)
66  if token:
67  _LOGGER.debug("found token for account %s", account_name)
69  hass,
70  account_name,
71  api_key,
72  shared_secret,
73  token,
74  stored_rtm_config,
75  component,
76  )
77  else:
79  hass, account_name, api_key, shared_secret, stored_rtm_config, component
80  )
81 
82  _LOGGER.debug("Finished adding all Remember the milk accounts")
83  return True
84 
85 
87  hass, account_name, api_key, shared_secret, token, stored_rtm_config, component
88 ):
89  entity = RememberTheMilkEntity(
90  account_name, api_key, shared_secret, token, stored_rtm_config
91  )
92  component.add_entities([entity])
93  hass.services.register(
94  DOMAIN,
95  f"{account_name}_create_task",
96  entity.create_task,
97  schema=SERVICE_SCHEMA_CREATE_TASK,
98  )
99  hass.services.register(
100  DOMAIN,
101  f"{account_name}_complete_task",
102  entity.complete_task,
103  schema=SERVICE_SCHEMA_COMPLETE_TASK,
104  )
105 
106 
108  hass, account_name, api_key, shared_secret, stored_rtm_config, component
109 ):
110  request_id = None
111  api = Rtm(api_key, shared_secret, "write", None)
112  url, frob = api.authenticate_desktop()
113  _LOGGER.debug("Sent authentication request to server")
114 
115  def register_account_callback(fields: list[dict[str, str]]) -> None:
116  """Call for register the configurator."""
117  api.retrieve_token(frob)
118  token = api.token
119  if api.token is None:
120  _LOGGER.error("Failed to register, please try again")
121  configurator.notify_errors(
122  hass, request_id, "Failed to register, please try again."
123  )
124  return
125 
126  stored_rtm_config.set_token(account_name, token)
127  _LOGGER.debug("Retrieved new token from server")
128 
130  hass,
131  account_name,
132  api_key,
133  shared_secret,
134  token,
135  stored_rtm_config,
136  component,
137  )
138 
139  configurator.request_done(hass, request_id)
140 
141  request_id = configurator.request_config(
142  hass,
143  f"{DOMAIN} - {account_name}",
144  callback=register_account_callback,
145  description=(
146  "You need to log in to Remember The Milk to"
147  "connect your account. \n\n"
148  "Step 1: Click on the link 'Remember The Milk login'\n\n"
149  "Step 2: Click on 'login completed'"
150  ),
151  link_name="Remember The Milk login",
152  link_url=url,
153  submit_caption="login completed",
154  )
155 
156 
158  """Internal configuration data for RememberTheMilk class.
159 
160  This class stores the authentication token it get from the backend.
161  """
162 
163  def __init__(self, hass):
164  """Create new instance of configuration."""
165  self._config_file_path_config_file_path = hass.config.path(CONFIG_FILE_NAME)
166  if not os.path.isfile(self._config_file_path_config_file_path):
167  self._config_config = {}
168  return
169  try:
170  _LOGGER.debug("Loading configuration from file: %s", self._config_file_path_config_file_path)
171  with open(self._config_file_path_config_file_path, encoding="utf8") as config_file:
172  self._config_config = json.load(config_file)
173  except ValueError:
174  _LOGGER.error(
175  "Failed to load configuration file, creating a new one: %s",
176  self._config_file_path_config_file_path,
177  )
178  self._config_config = {}
179 
180  def save_config(self):
181  """Write the configuration to a file."""
182  with open(self._config_file_path_config_file_path, "w", encoding="utf8") as config_file:
183  json.dump(self._config_config, config_file)
184 
185  def get_token(self, profile_name):
186  """Get the server token for a profile."""
187  if profile_name in self._config_config:
188  return self._config_config[profile_name][CONF_TOKEN]
189  return None
190 
191  def set_token(self, profile_name, token):
192  """Store a new server token for a profile."""
193  self._initialize_profile_initialize_profile(profile_name)
194  self._config_config[profile_name][CONF_TOKEN] = token
195  self.save_configsave_config()
196 
197  def delete_token(self, profile_name):
198  """Delete a token for a profile.
199 
200  Usually called when the token has expired.
201  """
202  self._config_config.pop(profile_name, None)
203  self.save_configsave_config()
204 
205  def _initialize_profile(self, profile_name):
206  """Initialize the data structures for a profile."""
207  if profile_name not in self._config_config:
208  self._config_config[profile_name] = {}
209  if CONF_ID_MAP not in self._config_config[profile_name]:
210  self._config_config[profile_name][CONF_ID_MAP] = {}
211 
212  def get_rtm_id(self, profile_name, hass_id):
213  """Get the RTM ids for a Home Assistant task ID.
214 
215  The id of a RTM tasks consists of the tuple:
216  list id, timeseries id and the task id.
217  """
218  self._initialize_profile_initialize_profile(profile_name)
219  ids = self._config_config[profile_name][CONF_ID_MAP].get(hass_id)
220  if ids is None:
221  return None
222  return ids[CONF_LIST_ID], ids[CONF_TIMESERIES_ID], ids[CONF_TASK_ID]
223 
224  def set_rtm_id(self, profile_name, hass_id, list_id, time_series_id, rtm_task_id):
225  """Add/Update the RTM task ID for a Home Assistant task IS."""
226  self._initialize_profile_initialize_profile(profile_name)
227  id_tuple = {
228  CONF_LIST_ID: list_id,
229  CONF_TIMESERIES_ID: time_series_id,
230  CONF_TASK_ID: rtm_task_id,
231  }
232  self._config_config[profile_name][CONF_ID_MAP][hass_id] = id_tuple
233  self.save_configsave_config()
234 
235  def delete_rtm_id(self, profile_name, hass_id):
236  """Delete a key mapping."""
237  self._initialize_profile_initialize_profile(profile_name)
238  if hass_id in self._config_config[profile_name][CONF_ID_MAP]:
239  del self._config_config[profile_name][CONF_ID_MAP][hass_id]
240  self.save_configsave_config()
def set_rtm_id(self, profile_name, hass_id, list_id, time_series_id, rtm_task_id)
Definition: __init__.py:224
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None open(self, **Any kwargs)
Definition: lock.py:86
bool setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:55
def _create_instance(hass, account_name, api_key, shared_secret, token, stored_rtm_config, component)
Definition: __init__.py:88
def _register_new_account(hass, account_name, api_key, shared_secret, stored_rtm_config, component)
Definition: __init__.py:109