Skip to content

Commit

Permalink
v1.0.0 Integration Release
Browse files Browse the repository at this point in the history
  • Loading branch information
Hantick committed Dec 30, 2024
1 parent 45fbe2b commit 6b24c9c
Show file tree
Hide file tree
Showing 17 changed files with 925 additions and 21 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Home Assistant TickTick Integration

![Static Badge](https://img.shields.io/badge/made%20with-fun-green?style=for-the-badge)‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎
![GitHub Repo stars](https://img.shields.io/github/stars/Hantick/ticktick-home-assistant?style=for-the-badge&color=%23AFB0CC)
![GitHub Release](https://img.shields.io/github/v/release/Hantick/ticktick-home-assistant?style=for-the-badge&color=%231CB00A)

Integration implements [TickTick Open API](https://developer.ticktick.com/docs#/openapi) and exposes it as a services in Home Assistant, allowing you to manage your tasks and projects programmatically 😎

## Installation

1. Navigate to [TickTick Developer](https://developer.ticktick.com/manage) and click `New App`
2. Name your app and set `OAuth redirect URL` to `https://my.home-assistant.io/redirect/oauth`
3. Add this repository in HACS and download TickTick Integration via HACS
4. Setup the integration in devices tab

## Exposed Services

### Task Services

Get, Create, Update, Delete, Complete Task

### Project Services

Get (Create, Update, Delete are missing for now)

## Left to be done:

- Create/Update Task Service: `items` - The list of subtasks
- Create/Update Task Service: `reminders` - Can create some better builder for reminders
- Create/Update Task Service: `repeatFlag` - Can create some better builder for reminders
- Get Project By ID Service
- Get Project By ID With Data Service
- Create Project
- Update Project
- Delete Project
- Some sensors and entities creation
109 changes: 96 additions & 13 deletions custom_components/ticktick/__init__.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,101 @@
"""
Configuration:
"""The TickTick Integration integration."""

To use the hello_world component you will need to add the following to your
configuration.yaml file.
ticktick:
"""
from __future__ import annotations

from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from .constants import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, SupportsResponse
from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow

from . import api
from .const import DOMAIN
from .service_handlers import (
handle_complete_task,
handle_create_task,
handle_update_task,
handle_delete_task,
handle_get_projects,
handle_get_task,
)
from .ticktick.ticktick_api import TickTickApiClient

type TickTickConfigEntry = ConfigEntry[api.AsyncConfigEntryAuth]


async def async_setup_entry(hass: HomeAssistant, entry: TickTickConfigEntry) -> bool:
"""Set up TickTick Integration from a config entry."""

implementation = (
await config_entry_oauth2_flow.async_get_config_entry_implementation(
hass, entry
)
)

session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)

# Using an aiohttp-based API lib
entry.runtime_data = api.AsyncConfigEntryAuth(
aiohttp_client.async_get_clientsession(hass), session
)

access_token = await entry.runtime_data.async_get_access_token()
tickTickApiClient = TickTickApiClient(entry.runtime_data._websession, access_token) # noqa: SLF001

await register_services(hass, tickTickApiClient)

platforms = [] # no platforms for now, change in the future TODO
await hass.config_entries.async_forward_entry_setups(entry, platforms)

return True


async def async_unload_entry(hass: HomeAssistant, entry: TickTickConfigEntry) -> bool:
"""Unload a TickTick config entry."""
return await hass.config_entries.async_unload(entry)


async def register_services(
hass: HomeAssistant, tickTickApiClient: TickTickApiClient
) -> None:
"""Register TickTick services."""

hass.services.async_register(
DOMAIN,
"get_task",
await handle_get_task(tickTickApiClient),
supports_response=SupportsResponse.OPTIONAL,
)
hass.services.async_register(
DOMAIN,
"create_task",
await handle_create_task(tickTickApiClient),
supports_response=SupportsResponse.OPTIONAL,
)
hass.services.async_register(
DOMAIN,
"update_task",
await handle_update_task(tickTickApiClient),
supports_response=SupportsResponse.OPTIONAL,
)
hass.services.async_register(
DOMAIN,
"complete_task",
await handle_complete_task(tickTickApiClient),
supports_response=SupportsResponse.OPTIONAL,
)
hass.services.async_register(
DOMAIN,
"delete_task",
await handle_delete_task(tickTickApiClient),
supports_response=SupportsResponse.OPTIONAL,
)

def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Setup of the TickTick integration."""
return True
hass.services.async_register(
DOMAIN,
"get_projects",
await handle_get_projects(tickTickApiClient),
supports_response=SupportsResponse.OPTIONAL,
)
# hass.services.async_register(DOMAIN, 'get_project', await handle_my_service)
# hass.services.async_register(DOMAIN, 'get_detailed_project', handle_my_service(tickTickApiClient))
# hass.services.async_register(DOMAIN, 'delete_project', handle_my_service(tickTickApiClient))
# hass.services.async_register(DOMAIN, 'create_project', handle_my_service(tickTickApiClient))
23 changes: 23 additions & 0 deletions custom_components/ticktick/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""API for TickTick Integration bound to Home Assistant OAuth."""

from aiohttp import ClientSession

from homeassistant.helpers import config_entry_oauth2_flow


class AsyncConfigEntryAuth:
"""Provide TickTick Integration authentication tied to an OAuth2 based config entry."""

def __init__(
self,
websession: ClientSession,
oauth_session: config_entry_oauth2_flow.OAuth2Session,
) -> None:
"""Initialize TickTick Integration auth."""
self._websession = websession # Store the web session for later use
self._oauth_session = oauth_session

async def async_get_access_token(self) -> str:
"""Return a valid access token."""
await self._oauth_session.async_ensure_token_valid()
return self._oauth_session.token["access_token"]
14 changes: 14 additions & 0 deletions custom_components/ticktick/application_credentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Application credentials platform for the TickTick Integration integration."""

from homeassistant.components.application_credentials import AuthorizationServer
from homeassistant.core import HomeAssistant

from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN


async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer:
"""Return authorization server."""
return AuthorizationServer(
authorize_url=OAUTH2_AUTHORIZE,
token_url=OAUTH2_TOKEN,
)
20 changes: 20 additions & 0 deletions custom_components/ticktick/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Config flow for TickTick Integration."""

import logging

from homeassistant.helpers import config_entry_oauth2_flow

from .const import DOMAIN


class OAuth2FlowHandler(
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
):
"""Config flow to handle TickTick Integration OAuth2 authentication."""

DOMAIN = DOMAIN

@property
def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)
25 changes: 25 additions & 0 deletions custom_components/ticktick/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Constants for the TickTick Integration integration."""

DOMAIN = "ticktick"

OAUTH2_AUTHORIZE = "https://ticktick.com/oauth/authorize"
OAUTH2_TOKEN = "https://ticktick.com/oauth/token"
TICKTICK_HOST = "api.ticktick.com"
API = "open/v1"
BASE_API_URL = f"{TICKTICK_HOST}/{API}"

# === Parameters === #
PROJECT_ID = "projectId"
TASK_ID = "taskId"

# === Endpoints === #

# === Task Scope ===
GET_TASK = f"{BASE_API_URL}/project/{{{PROJECT_ID}}}/task/{{{TASK_ID}}}"
CREATE_TASK = f"{BASE_API_URL}/task"
UPDATE_TASK = f"{CREATE_TASK}/{{{TASK_ID}}}"
COMPLETE_TASK = f"{BASE_API_URL}/project/{{{PROJECT_ID}}}/task/{{{TASK_ID}}}/complete"
DELETE_TASK = GET_TASK

# === Project Scope ===
GET_PROJECTS = f"{BASE_API_URL}/project"
3 changes: 0 additions & 3 deletions custom_components/ticktick/constants.py

This file was deleted.

22 changes: 22 additions & 0 deletions custom_components/ticktick/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"services": {
"get_task": {
"service": "mdi:invoice-list"
},
"create_task": {
"service": "mdi:creation"
},
"update_task": {
"service": "mdi:arrow-up-bold-hexagon-outline"
},
"complete_task": {
"service": "mdi:check-decagram"
},
"delete_task": {
"service": "mdi:delete"
},
"get_projects": {
"service": "mdi:list-box"
}
}
}
17 changes: 12 additions & 5 deletions custom_components/ticktick/manifest.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
{
"domain": "ticktick",
"name": "TickTick",
"codeowners": ["@Hantick"],
"codeowners": [
"@Hantick"
],
"config_flow": true,
"dependencies": [
"application_credentials"
],
"documentation": "/~https://github.com/Hantick/ticktick-home-assistant",
"issue_tracker": "/~https://github.com/Hantick/ticktick-home-assistant/issues",
"requirements": [
"requests<3.0.0"
],
"homekit": {},
"iot_class": "cloud_polling",
"requirements": [],
"ssdp": [],
"zeroconf": [],
"integration_type": "service",
"version": "0.1.0"
"version": "1.0.0"
}
60 changes: 60 additions & 0 deletions custom_components/ticktick/quality_scale.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
rules:
# Bronze
action-setup: todo
appropriate-polling: todo
brands: todo
common-modules: todo
config-flow-test-coverage: todo
config-flow: todo
dependency-transparency: todo
docs-actions: todo
docs-high-level-description: todo
docs-installation-instructions: todo
docs-removal-instructions: todo
entity-event-setup: todo
entity-unique-id: todo
has-entity-name: todo
runtime-data: todo
test-before-configure: todo
test-before-setup: todo
unique-config-entry: todo

# Silver
action-exceptions: todo
config-entry-unloading: todo
docs-configuration-parameters: todo
docs-installation-parameters: todo
entity-unavailable: todo
integration-owner: todo
log-when-unavailable: todo
parallel-updates: todo
reauthentication-flow: todo
test-coverage: todo

# Gold
devices: todo
diagnostics: todo
discovery-update-info: todo
discovery: todo
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
docs-supported-devices: todo
docs-supported-functions: todo
docs-troubleshooting: todo
docs-use-cases: todo
dynamic-devices: todo
entity-category: todo
entity-device-class: todo
entity-disabled-by-default: todo
entity-translations: todo
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
repair-issues: todo
stale-devices: todo

# Platinum
async-dependency: todo
inject-websession: todo
strict-typing: todo
Loading

0 comments on commit 6b24c9c

Please sign in to comment.