-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 77ae472
Showing
17 changed files
with
549 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"image": "homeassistant/home-assistant:latest", | ||
"customizations": { | ||
"vscode": { | ||
"extensions": [ | ||
"ms-python.python", | ||
"ms-python.autopep8" | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
name: Validate HACS | ||
|
||
on: | ||
push: | ||
pull_request: | ||
|
||
jobs: | ||
validate-hacs: | ||
runs-on: "ubuntu-latest" | ||
steps: | ||
- uses: "actions/checkout@v3" | ||
- name: HACS validation | ||
uses: "hacs/action@main" | ||
with: | ||
category: "integration" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
name: Validate with hassfest | ||
|
||
on: | ||
push: | ||
pull_request: | ||
|
||
jobs: | ||
validate: | ||
runs-on: "ubuntu-latest" | ||
steps: | ||
- uses: "actions/checkout@v3" | ||
- uses: home-assistant/actions/hassfest@master |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
custom_components/__pycache__ | ||
custom_components/mikan/__pycache__ | ||
tests/__pycache__ | ||
|
||
.pytest_cache | ||
.mypy_cache | ||
.ruff_cache | ||
.coverage | ||
.vscode/ | ||
|
||
test_hass/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
repos: | ||
- repo: https://github.com/asottile/pyupgrade | ||
rev: v3.15.2 | ||
hooks: | ||
- id: pyupgrade | ||
args: [--py37-plus] | ||
- repo: https://github.com/psf/black | ||
rev: 24.4.2 | ||
hooks: | ||
- id: black | ||
args: | ||
- --safe | ||
- --quiet | ||
files: ^((homeassistant|script|tests)/.+)?[^/]+\.py$ | ||
- repo: https://github.com/codespell-project/codespell | ||
rev: v2.2.6 | ||
hooks: | ||
- id: codespell | ||
args: | ||
- --ignore-words-list=hass,alot,datas,dof,dur,farenheit,hist,iff,ines,ist,lightsensor,mut,nd,pres,referer,ser,serie,te,technik,ue,uint,visability,wan,wanna,withing | ||
- --skip="./.*,*.csv,*.json" | ||
- --quiet-level=2 | ||
exclude_types: [csv, json] | ||
- repo: https://github.com/charliermarsh/ruff-pre-commit | ||
rev: v0.4.8 | ||
hooks: | ||
- id: ruff | ||
args: [--fix, --exit-non-zero-on-fix] | ||
- id: ruff-format | ||
- repo: https://github.com/pre-commit/mirrors-mypy | ||
rev: v1.10.0 | ||
hooks: | ||
- id: mypy | ||
args: | ||
- --pretty | ||
- --show-error-codes | ||
- --show-error-context |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 Cerallin | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
## 蜜柑计划(Mikanani)的 homeassistant 集成 | ||
|
||
This is a hass integration for [Mikanani](https://mikanani.me). | ||
|
||
### 功能 | ||
|
||
- 提供新番时间表传感器`sensor.bangumi_map`。 | ||
|
||
键是一个一位十进制数字,代表星期N或者是剧场版。0-6 代表从周日开始的一周,7 代表剧场版。 | ||
|
||
0-6: 周日-周六 | ||
7 : 剧场版 | ||
|
||
### 安装 | ||
|
||
[![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg)](https://github.com/hacs/integration) | ||
|
||
打开HACS设置并添加本repo (https://github.com/Cerallin/hass-rennigou) 为一个自定义集成(分类要选**Integration**) | ||
|
||
你也可以点击下方按钮一键安装: | ||
[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?category=Integration&repository=hass-rennigou&owner=Cerallin) | ||
|
||
## 配置 | ||
|
||
在`config.yaml`里添加一行 | ||
```yaml | ||
mikanani: true | ||
``` | ||
之后重启HA。 | ||
(目前没什么配置,后续再添加一些选项) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.typing import ConfigType | ||
from homeassistant.helpers.event import async_track_time_interval | ||
from homeassistant.helpers.discovery import async_load_platform | ||
|
||
from .const import DOMAIN, LOGGER, DEFAULT_SCAN_INTERVAL | ||
from .coodinator import MikanCoordinator | ||
|
||
async def async_setup(hass: HomeAssistant, config: ConfigType): | ||
"""Set up the component.""" | ||
LOGGER.info("Ciallo~(∠・ω< ) Mikan-Ani") | ||
|
||
coordinator = MikanCoordinator(hass) | ||
await coordinator.async_config_entry_first_refresh() | ||
|
||
hass.data.setdefault(DOMAIN, {})[0] = coordinator | ||
|
||
await async_load_platform(hass, 'sensor', DOMAIN, { | ||
"coordinator": coordinator | ||
}, {}) | ||
|
||
async def async_update_data(now): | ||
await coordinator.async_request_refresh() | ||
|
||
async_track_time_interval(hass, async_update_data, DEFAULT_SCAN_INTERVAL) | ||
|
||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import logging | ||
from datetime import timedelta | ||
|
||
LOGGER = logging.getLogger(__package__) | ||
DEFAULT_SCAN_INTERVAL = timedelta(hours=1) | ||
|
||
DOMAIN = "mikanani" | ||
|
||
ATTRIBUTION = "第三方蜜柑计划 ha 集成" | ||
|
||
MIKAN_HOST = "https://mikanani.me" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"""Coordinator for mikanani.""" | ||
|
||
import aiohttp | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed | ||
|
||
from .const import ( | ||
DOMAIN, | ||
MIKAN_HOST, | ||
LOGGER, | ||
DEFAULT_SCAN_INTERVAL, | ||
) | ||
from .mikan import MikanBangumi, MikanHTMLParser, MikanParseResult | ||
|
||
|
||
class MikanCoordinator(DataUpdateCoordinator[MikanParseResult]): | ||
"""Class to manage fetching mikanani data.""" | ||
|
||
config_entry: ConfigEntry | ||
|
||
def __init__(self, hass: HomeAssistant) -> None: | ||
"""Initialize.""" | ||
super().__init__( | ||
hass, | ||
LOGGER, | ||
name=DOMAIN, | ||
update_interval=DEFAULT_SCAN_INTERVAL, | ||
) | ||
|
||
self._parser = MikanHTMLParser() | ||
|
||
async def update_token(self): | ||
pass | ||
|
||
async def _async_request(self): | ||
async with aiohttp.ClientSession() as session: | ||
async with session.request("GET", MIKAN_HOST) as response: | ||
return await response.text(encoding="UTF-8") | ||
|
||
async def _async_update_data(self) -> MikanParseResult: | ||
parser = MikanHTMLParser() | ||
|
||
try: | ||
html_text = await self._async_request() | ||
parser.feed(html_text) | ||
except Exception as err: | ||
LOGGER.exception(err) | ||
raise UpdateFailed(err) from err | ||
|
||
return parser.parse_result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"domain": "mikanani", | ||
"name": "Mikanani", | ||
"codeowners": ["@Cerallin"], | ||
"dependencies": [], | ||
"documentation": "https://github.com/Cerallin/hass-mikan/blob/master/README.md", | ||
"integration_type": "service", | ||
"iot_class": "cloud_polling", | ||
"issue_tracker": "https://github.com/Cerallin/hass-mikan/issues", | ||
"requirements": [], | ||
"version": "0.1.0" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
from html.parser import HTMLParser | ||
from dataclasses import dataclass | ||
from enum import Enum | ||
|
||
from .const import MIKAN_HOST | ||
|
||
type MikanParseResult = dict[int | str, list[MikanBangumi]] | ||
|
||
|
||
class ParseStates(Enum): | ||
START = 0 | ||
DAILY_ENTRY = 1 | ||
BANGUMI_ENTRY = 2 | ||
|
||
|
||
@dataclass | ||
class MikanBangumi: | ||
id: int | ||
title: str | ||
link: str | ||
image_link: str | ||
subscribed: bool | ||
|
||
def __init__(self) -> None: | ||
self.id: int = 0 | ||
self.title: str = "" | ||
self.link: str = "" | ||
self.image_link: str = "" | ||
self.subscribed: bool = False | ||
|
||
|
||
class MikanHTMLParser(HTMLParser): | ||
"""MikanHTMLParser: 解析 Mikanani 网页番剧信息。 | ||
Usage: | ||
html_text = request_func(MIKAN_HOST) | ||
parser = MikanHTMLParser() | ||
parser.feed(html_text) | ||
print(parser.parse_result) | ||
使用简陋的有限状态机实现番剧信息的提取。 | ||
提取结果类型为 dict[str, list[MikanBangumi]],该字典的值是一个MikanBangumi列表, | ||
键通常是一个一位十进制数字,代表星期N/剧场版。0-6代表从周日开始的一周,7代表剧场版。 | ||
0-6: 周日-周六 | ||
7 : 剧场版 | ||
""" | ||
|
||
def __init__(self) -> None: | ||
super().__init__() | ||
|
||
self._bangumi_map : MikanParseResult = {} | ||
|
||
self._state = ParseStates.START | ||
self._week = "" | ||
|
||
@property | ||
def _bangumi(self) -> MikanBangumi: | ||
"""当前正需记录信息的番剧""" | ||
return self._bangumi_map[self._week][-1] | ||
|
||
@property | ||
def parse_result(self) -> MikanParseResult: | ||
"""解析结果""" | ||
return self._bangumi_map | ||
|
||
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]): | ||
"""从标签解析信息""" | ||
|
||
# 不需要考虑重 key 的问题,所以转成 dict | ||
attributes = dict(attrs) | ||
|
||
# 一周番剧列表从 div 开始 | ||
if tag == 'div': | ||
# 如果 div 有 data-dayofweek 属性则设状态为 DAILY_ENTRY | ||
if (week := attributes.get("data-dayofweek")) is not None: | ||
self._state = ParseStates.DAILY_ENTRY | ||
# 记录 week 编号 | ||
# 0-6 为周日-周六 | ||
# 7 为剧场版 | ||
self._week = int(week) if str.isdigit(week) else week | ||
# 初始化本周番剧列表为空数组 | ||
if not self._bangumi_map.get(week): | ||
self._bangumi_map[self._week] = [] | ||
|
||
# 番剧从 li 开始 | ||
if self._state == ParseStates.DAILY_ENTRY and tag == "li": | ||
# 设状态为 BANGUMI_ENTRY | ||
self._state = ParseStates.BANGUMI_ENTRY | ||
# 初始化 MikanBangumi,之后填空 | ||
self._bangumi_map[self._week].append(MikanBangumi()) | ||
|
||
# 当状态为 BANGUMI_ENTRY,开始记录番剧信息 | ||
if self._state == ParseStates.BANGUMI_ENTRY: | ||
if tag == "span": | ||
# 封面图片链接 | ||
if (image_link := attributes.get("data-src")) is not None: | ||
self._bangumi.image_link = image_link | ||
# 是否已订阅 | ||
self._bangumi.subscribed = attributes.get("data-showsubscribed") == "true" | ||
# bangumi id, 如果是数字才记录 | ||
if (id := attributes.get("data-bangumiid")) is not None and str.isdigit(id): | ||
self._bangumi.id = int(id) | ||
# 以防万一有多个a标签,要的是有title属性的a标签 | ||
elif tag == "a" and (title := attributes.get("title")) is not None: | ||
# 番剧链接 | ||
if (link := attributes.get("href")) is not None: | ||
self._bangumi.link = MIKAN_HOST + link | ||
# 番剧标题 | ||
self._bangumi.title = title | ||
|
||
def handle_endtag(self, tag): | ||
# 当 li 闭合的时候结束 BANGUMI_ENTRY,回到 DAILY_ENTRY | ||
if self._state == ParseStates.BANGUMI_ENTRY and tag == "li": | ||
self._state = ParseStates.DAILY_ENTRY |
Oops, something went wrong.