├── .gitignore ├── custom_components └── ha_bark │ ├── icons │ └── icon.png │ ├── const.py │ ├── manifest.json │ ├── translations │ ├── en.json │ └── zh-Hans.json │ ├── __init__.py │ ├── config_flow.py │ └── notify.py ├── hacs.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /custom_components/ha_bark/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanh7/ha-bark/HEAD/custom_components/ha_bark/icons/icon.png -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bark", 3 | "homeassistant": "0.115.0", 4 | "country": "CN", 5 | "domains": ["notify"], 6 | "render_readme": true 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HA Bark 2 | [![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg)](https://github.com/hacs/integration) 3 | 4 | 适用于home assistant的插件,可通过hacs添加。 -------------------------------------------------------------------------------- /custom_components/ha_bark/const.py: -------------------------------------------------------------------------------- 1 | """Constants for the Yamaha component.""" 2 | DOMAIN = "ha_bark" 3 | DATA_BARK = "bark_data" 4 | ATTR_COPY = "copy" 5 | ATTR_SOUND = "sound" 6 | ATTR_ICON = "icon" 7 | ATTR_GROUP = "group" 8 | ATTR_URL = "url" 9 | ATTR_BADGE = "badge" 10 | ATTR_AUTO_COPY = "autocopy" -------------------------------------------------------------------------------- /custom_components/ha_bark/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "ha_bark", 3 | "name": "Bark", 4 | "documentation": "https://github.com/ryanh7/ha-bark", 5 | "requirements": [], 6 | "codeowners": ["@ryanh7"], 7 | "config_flow": true, 8 | "iot_class": "local_polling", 9 | "version": "1.0.0" 10 | } 11 | -------------------------------------------------------------------------------- /custom_components/ha_bark/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "step": { 4 | "user": { 5 | "data": { 6 | "name": "Name", 7 | "host": "Host, like https://api.day.app", 8 | "token": "Device token" 9 | }, 10 | "title": "Setup" 11 | } 12 | } 13 | }, 14 | "options": { 15 | "step": { 16 | "init": { 17 | "data": { 18 | "name": "Name", 19 | "host": "Host, like https://api.day.app", 20 | "token": "Device token" 21 | }, 22 | "title": "Setup" 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /custom_components/ha_bark/translations/zh-Hans.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "step": { 4 | "user": { 5 | "data": { 6 | "name": "名称", 7 | "host": "服务器域名或IP, 如https://api.day.app", 8 | "token": "Device token, 如https://api.day.app/xxxx中的xxxx部分" 9 | }, 10 | "title": "Bark设置" 11 | } 12 | } 13 | }, 14 | "options": { 15 | "step": { 16 | "init": { 17 | "data": { 18 | "name": "名称", 19 | "host": "服务器域名或IP, 如https://api.day.app", 20 | "token": "Device token, 如https://api.day.app/xxxx中的xxxx部分" 21 | }, 22 | "title": "Bark设置" 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /custom_components/ha_bark/__init__.py: -------------------------------------------------------------------------------- 1 | from homeassistant.config_entries import ConfigEntry 2 | from homeassistant.const import Platform, CONF_NAME, CONF_URL, CONF_HOST, CONF_TOKEN 3 | from homeassistant.core import HomeAssistant 4 | from homeassistant.helpers import discovery 5 | from homeassistant.helpers.typing import ConfigType 6 | from homeassistant.components import notify as hass_notify 7 | from .const import DOMAIN, DATA_BARK 8 | 9 | PLATFORMS = [Platform.NOTIFY] 10 | 11 | 12 | async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: 13 | hass.data.setdefault(DATA_BARK, {}) 14 | hass.async_create_task( 15 | discovery.async_load_platform( 16 | hass, Platform.NOTIFY, DOMAIN, {CONF_NAME: "bark"}, config 17 | ) 18 | ) 19 | return True 20 | 21 | 22 | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: 23 | config = entry.data 24 | hass.data[DATA_BARK][config[CONF_NAME]] = config 25 | await hass_notify.async_reload(hass, DOMAIN) 26 | return True 27 | 28 | 29 | async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: 30 | """Unload a config entry.""" 31 | config = entry.data 32 | hass.data[DATA_BARK].pop(config[CONF_NAME]) 33 | await hass_notify.async_reload(hass, DOMAIN) 34 | 35 | return True 36 | 37 | 38 | async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: 39 | """Handle an options update.""" 40 | config = entry.data 41 | hass.data[DATA_BARK][config[CONF_NAME]] = config 42 | await hass_notify.async_reload(hass, DOMAIN) 43 | return True 44 | 45 | 46 | async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: 47 | """Migrate old entry.""" 48 | if config_entry.version == 1: 49 | data = {**config_entry.data} 50 | url = data[CONF_URL] 51 | index = url.rindex("/") 52 | data[CONF_HOST] = url[:index] 53 | data[CONF_TOKEN] = url[index + 1:] 54 | data.pop(CONF_URL) 55 | config_entry.version = 2 56 | hass.config_entries.async_update_entry( 57 | config_entry, 58 | data=data, 59 | ) 60 | 61 | return True 62 | -------------------------------------------------------------------------------- /custom_components/ha_bark/config_flow.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import logging 3 | 4 | import voluptuous as vol 5 | from homeassistant import config_entries 6 | from homeassistant.core import callback 7 | import homeassistant.helpers.config_validation as cv 8 | from homeassistant.const import CONF_NAME, CONF_HOST, CONF_TOKEN 9 | 10 | from .const import DOMAIN 11 | 12 | _LOGGER = logging.getLogger(__name__) 13 | 14 | 15 | class BarkFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): 16 | """Config flow for Met Eireann component.""" 17 | 18 | VERSION = 2 19 | 20 | async def async_step_user(self, user_input=None): 21 | """Handle a flow initialized by the user.""" 22 | errors = {} 23 | 24 | if user_input is not None: 25 | return self.async_create_entry(title=user_input[CONF_NAME], data=user_input) 26 | 27 | return self.async_show_form( 28 | step_id="user", 29 | data_schema=vol.Schema( 30 | { 31 | vol.Required(CONF_NAME): cv.string, 32 | vol.Required(CONF_HOST, default="https://api.day.app"): cv.string, 33 | vol.Required(CONF_TOKEN): cv.string, 34 | } 35 | ), 36 | errors=errors, 37 | ) 38 | 39 | @staticmethod 40 | @callback 41 | def async_get_options_flow(config_entry): 42 | """Get the options flow for this handler.""" 43 | return OptionsFlowHandler(config_entry) 44 | 45 | 46 | class OptionsFlowHandler(config_entries.OptionsFlow): 47 | def __init__(self, config_entry: config_entries.ConfigEntry) -> None: 48 | """Initialize options flow.""" 49 | self.config_entry = config_entry 50 | self.config = dict(config_entry.data) 51 | 52 | async def async_step_init(self, user_input=None): 53 | if user_input is not None: 54 | self.config.update(user_input) 55 | self.hass.config_entries.async_update_entry( 56 | self.config_entry, 57 | data=self.config 58 | ) 59 | await self.hass.config_entries.async_reload(self.config_entry.entry_id) 60 | return self.async_create_entry(title="", data=self.config) 61 | 62 | return self.async_show_form( 63 | step_id="init", 64 | data_schema=vol.Schema( 65 | { 66 | vol.Required(CONF_NAME, default=self.config[CONF_NAME]): cv.string, 67 | vol.Required(CONF_HOST, default=self.config[CONF_HOST]): cv.string, 68 | vol.Required(CONF_TOKEN, default=self.config[CONF_TOKEN]): cv.string, 69 | } 70 | ) 71 | ) 72 | -------------------------------------------------------------------------------- /custom_components/ha_bark/notify.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import requests 3 | 4 | from homeassistant.components.notify import ( 5 | ATTR_DATA, 6 | ATTR_TARGET, 7 | ATTR_TITLE, 8 | ATTR_TITLE_DEFAULT, 9 | BaseNotificationService 10 | ) 11 | from homeassistant.const import CONF_HOST, CONF_TOKEN 12 | from .const import ATTR_AUTO_COPY, ATTR_BADGE, ATTR_COPY, ATTR_GROUP, ATTR_ICON, ATTR_SOUND, ATTR_URL, DATA_BARK 13 | 14 | _LOGGER = logging.getLogger(__name__) 15 | 16 | 17 | def get_service(hass, config, discovery_info=None): 18 | return BarkNotificationService(hass) 19 | 20 | 21 | class BarkNotificationService(BaseNotificationService): 22 | 23 | def __init__(self, hass): 24 | """Initialize the service.""" 25 | self.hass = hass 26 | 27 | @property 28 | def targets(self): 29 | """Return a dictionary of registered targets.""" 30 | targets = {} 31 | for name in self.hass.data[DATA_BARK].keys(): 32 | targets[name] = name 33 | return targets 34 | 35 | def send_message(self, message="", **kwargs): 36 | if not (targets := kwargs.get(ATTR_TARGET)): 37 | targets = self.hass.data[DATA_BARK].keys() 38 | 39 | for name in targets: 40 | if (config := self.hass.data[DATA_BARK].get(name)) is None: 41 | continue 42 | 43 | params = {} 44 | params["body"] = message 45 | params["device_key"] = config[CONF_TOKEN] 46 | 47 | if ( 48 | (title := kwargs.get(ATTR_TITLE)) is not None 49 | and title != ATTR_TITLE_DEFAULT 50 | ): 51 | params["title"] = title 52 | 53 | if (data := kwargs.get(ATTR_DATA)) is not None: 54 | if (copy := data.get(ATTR_COPY)) is not None: 55 | params["copy"] = copy 56 | if data.get(ATTR_AUTO_COPY): 57 | params["automaticallyCopy"] = 1 58 | if (badge := data.get(ATTR_BADGE)) is not None: 59 | params["badge"] = badge 60 | if (purl := data.get(ATTR_URL)) is not None: 61 | params["url"] = purl 62 | if (group := data.get(ATTR_GROUP)) is not None: 63 | params["group"] = group 64 | if (icon := data.get(ATTR_ICON)) is not None: 65 | params["icon"] = icon 66 | if (sound := data.get(ATTR_SOUND)) is not None: 67 | params["sound"] = sound 68 | 69 | try: 70 | resp = requests.post( 71 | url=config[CONF_HOST] + "/push", 72 | json=params 73 | ) 74 | 75 | result = resp.json() 76 | if result.get("code") != 200: 77 | _LOGGER.warning(result) 78 | except Exception as e: 79 | _LOGGER.error(e) 80 | --------------------------------------------------------------------------------