├── .devcontainer.json ├── .gitattributes ├── .github ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── hassfest.yml │ ├── release-drafter.yml │ ├── release.yml │ └── validate.yml ├── .gitignore ├── .ruff.toml ├── .vscode └── tasks.json ├── LICENSE.txt ├── README.md ├── config └── configuration.yaml ├── custom_components └── simpleicons │ ├── __init__.py │ ├── config_flow.py │ ├── data │ └── si.js │ ├── manifest.json │ └── translations │ ├── de.json │ ├── en.json │ ├── nb.json │ └── pl.json ├── hacs.json ├── manage └── update_manifest.py ├── requirements.txt └── scripts ├── develop ├── lint └── setup /.devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ludeeus/integration_blueprint", 3 | "image": "mcr.microsoft.com/devcontainers/python:3.12", 4 | "postCreateCommand": "scripts/setup", 5 | "forwardPorts": [ 6 | 8123 7 | ], 8 | "portsAttributes": { 9 | "8123": { 10 | "label": "Home Assistant", 11 | "onAutoForward": "notify" 12 | } 13 | }, 14 | "customizations": { 15 | "vscode": { 16 | "extensions": [ 17 | "charliermarsh.ruff", 18 | "github.vscode-pull-request-github", 19 | "ms-python.python", 20 | "ms-python.vscode-pylance", 21 | "ryanluker.vscode-coverage-gutters" 22 | ], 23 | "settings": { 24 | "files.eol": "\n", 25 | "editor.tabSize": 4, 26 | "editor.formatOnPaste": true, 27 | "editor.formatOnSave": true, 28 | "editor.formatOnType": false, 29 | "files.trimTrailingWhitespace": true, 30 | "python.analysis.typeCheckingMode": "basic", 31 | "python.analysis.autoImportCompletions": true, 32 | "python.defaultInterpreterPath": "/usr/local/bin/python", 33 | "[python]": { 34 | "editor.defaultFormatter": "charliermarsh.ruff" 35 | } 36 | } 37 | } 38 | }, 39 | "remoteUser": "vscode", 40 | "features": {} 41 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | custom_components/**/*.js binary 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: webpack-cli 11 | versions: 12 | - 4.5.0 13 | - dependency-name: webpack 14 | versions: 15 | - 5.18.0 16 | - 5.19.0 17 | - 5.20.0 18 | - 5.20.1 19 | - 5.20.2 20 | - 5.21.2 21 | - 5.22.0 22 | - 5.23.0 23 | - 5.24.0 24 | - 5.24.1 25 | - 5.24.2 26 | - 5.24.3 27 | - 5.24.4 28 | - package-ecosystem: "pip" 29 | directory: "/" 30 | schedule: 31 | interval: "daily" 32 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: v$NEXT_MINOR_VERSION 2 | tag-template: v$NEXT_MINOR_VERSION 3 | categories: 4 | - title: ⬆️ Dependencies 5 | label: dependencies 6 | - title: 🚀 Features 7 | label: feature 8 | - title: 🐛 Bug Fixes 9 | label: fix 10 | - title: 🧰 Maintenance 11 | label: chore 12 | 13 | version-resolver: 14 | major: 15 | labels: 16 | - 'major' 17 | - 'feature' 18 | minor: 19 | labels: 20 | - 'minor' 21 | - 'fix' 22 | patch: 23 | labels: 24 | - 'patch' 25 | - 'dependencies' 26 | - 'chore' 27 | default: patch 28 | 29 | template: | 30 | ## Changes 31 | 32 | $CHANGES 33 | -------------------------------------------------------------------------------- /.github/workflows/hassfest.yml: -------------------------------------------------------------------------------- 1 | name: Validate with hassfest 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | validate: 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - uses: "actions/checkout@v2" 14 | - uses: home-assistant/actions/hassfest@master -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | release_zip_file: 9 | name: Prepare release asset 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check out repository 13 | uses: actions/checkout@v2 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: 3.8 19 | 20 | - name: Get version 21 | id: version 22 | uses: home-assistant/actions/helpers/version@master 23 | 24 | - name: "Set version number" 25 | run: | 26 | python3 ${{ github.workspace }}/manage/update_manifest.py --version ${{ steps.version.outputs.version }} 27 | # Pack the simpleicons dir as a zip and upload to the release 28 | - name: ZIP simpleicons Dir 29 | run: | 30 | cd ${{ github.workspace }}/custom_components/simpleicons 31 | zip hass-simpleicons.zip -r ./ 32 | - name: Upload zip to release 33 | uses: svenstaro/upload-release-action@v1-release 34 | 35 | with: 36 | repo_token: ${{ secrets.GITHUB_TOKEN }} 37 | file: ${{ github.workspace }}/custom_components/simpleicons/hass-simpleicons.zip 38 | asset_name: hass-simpleicons.zip 39 | tag: ${{ github.ref }} 40 | overwrite: true 41 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate with hacs 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | validate: 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - uses: "actions/checkout@v2" 14 | - name: HACS validation 15 | uses: "hacs/action@main" 16 | with: 17 | category: "integration" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | **/__pycache__ 3 | venv/ -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | # The contents of this file is based on https://github.com/home-assistant/core/blob/dev/pyproject.toml 2 | 3 | target-version = "py312" 4 | 5 | [lint] 6 | select = [ 7 | "ALL", 8 | ] 9 | 10 | ignore = [ 11 | "ANN101", # Missing type annotation for `self` in method 12 | "ANN401", # Dynamically typed expressions (typing.Any) are disallowed 13 | "D203", # no-blank-line-before-class (incompatible with formatter) 14 | "D212", # multi-line-summary-first-line (incompatible with formatter) 15 | "COM812", # incompatible with formatter 16 | "ISC001", # incompatible with formatter 17 | ] 18 | 19 | [lint.flake8-pytest-style] 20 | fixture-parentheses = false 21 | 22 | [lint.pyupgrade] 23 | keep-runtime-typing = true 24 | 25 | [lint.mccabe] 26 | max-complexity = 25 -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Run Home Assistant on port 8123", 6 | "type": "shell", 7 | "command": "scripts/develop", 8 | "problemMatcher": [] 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tom Schneider 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hass-simpleicons 2 | 3 | [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)]([https://github.com/custom-components/hacs](https://github.com/hacs/integration)) 4 | 5 | Use free icons from the [simpleicons](https://simpleicons.org) set in Home Assistant. 6 | 7 | ## Installation instructions 8 | 9 | - Install using [HACS](https://hacs.xyz) (Or copy the contents of `custom_components/simpleicons/` to `/custom_components/simpleicons/`.) 10 | 11 | [![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/?owner=vigonotion&repository=hass-simpleicons) 12 | - Restart Home Assistant 13 | 14 | - Go to your integrations configuration and add simpleicons 15 | 16 | [![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=simpleicons) 17 | 18 | ## Usage 19 | 20 | Find the icon you want in the [gallery](http://simpleicons.org/). Click on an icon. This will download the icon. You don't need that file, just it's filename. 21 | 22 | The icon set is prefixed by: `si:`. Then comes the filename. 23 | 24 | So, 25 | 26 | - to get a facebook logo, use `si:facebook` 27 | - to get a zigbee logo, use `si:zigbee` 28 | - and so on 29 | 30 | The icons are useable anywhere in Home Assistant - not only in lovelace. 31 | 32 | ## FAQ 33 | 34 | ### Can I set this up in configure.yaml instead? 35 | 36 | Yes. 37 | 38 | ```yaml 39 | simpleicons: 40 | ``` 41 | 42 | ## Special thanks 43 | 44 | This work is heavily based on [hass-fontawesome](https://github.com/thomasloven/hass-fontawesome) by Thomas Loven. Thank you! 45 | -------------------------------------------------------------------------------- /config/configuration.yaml: -------------------------------------------------------------------------------- 1 | # https://www.home-assistant.io/integrations/default_config/ 2 | default_config: 3 | 4 | # https://www.home-assistant.io/integrations/homeassistant/ 5 | homeassistant: 6 | debug: true 7 | -------------------------------------------------------------------------------- /custom_components/simpleicons/__init__.py: -------------------------------------------------------------------------------- 1 | from xml.dom.minidom import Document, parseString 2 | 3 | from homeassistant.components.http import StaticPathConfig 4 | from homeassistant.components.http.view import HomeAssistantView 5 | from homeassistant.config_entries import SOURCE_IMPORT 6 | from homeassistant.helpers import config_validation as cv 7 | 8 | from simplepycons import all_icons 9 | 10 | DOMAIN = "simpleicons" 11 | 12 | DATA_EXTRA_MODULE_URL = "frontend_extra_module_url" 13 | ICONS_URL = "/" + DOMAIN + "/" 14 | ICON_URL = f"/{DOMAIN}/icons" 15 | ICON_FILES = {"simpleicons": "si.js"} 16 | 17 | CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) 18 | 19 | class IconView(HomeAssistantView): 20 | requires_auth = False 21 | 22 | def __init__(self, slug): 23 | self.url = ICON_URL + "/" + slug 24 | icon = all_icons[slug] 25 | dom = parseString(icon.raw_svg) 26 | self.path = dom.getElementsByTagName("path")[0].getAttribute("d") 27 | 28 | self.name = "Icon View" 29 | 30 | async def get(self, request): 31 | return self.json({"path": self.path}) 32 | 33 | 34 | class ListView(HomeAssistantView): 35 | requires_auth = False 36 | 37 | def __init__(self): 38 | self.url = ICON_URL 39 | self.name = "Icons View" 40 | 41 | async def get(self, request): 42 | return self.json( 43 | [{"name": icon.prototype.name} for icon in all_icons.__dict__.values()] 44 | ) 45 | 46 | 47 | async def async_setup(hass, config): 48 | await hass.http.async_register_static_paths( 49 | [ 50 | StaticPathConfig( 51 | f"/{DOMAIN}/si.js", 52 | hass.config.path(f"custom_components/{DOMAIN}/data/si.js"), 53 | True, 54 | ) 55 | ] 56 | ) 57 | 58 | hass.http.register_view(ListView()) 59 | 60 | for icon in all_icons.__dict__.values(): 61 | hass.http.register_view(IconView(icon.prototype.name)) 62 | 63 | if DOMAIN not in config: 64 | return True 65 | 66 | hass.async_create_task( 67 | hass.config_entries.flow.async_init(DOMAIN, context={"source": SOURCE_IMPORT}) 68 | ) 69 | 70 | register_modules(hass) 71 | return True 72 | 73 | 74 | async def async_setup_entry(hass, config_entry): 75 | config_entry.add_update_listener(_update_listener) 76 | register_modules(hass) 77 | return True 78 | 79 | 80 | async def async_remove_entry(hass, config_entry): 81 | register_modules(hass) 82 | return True 83 | 84 | 85 | async def _update_listener(hass, config_entry): 86 | register_modules(hass) 87 | return True 88 | 89 | 90 | def register_modules(hass): 91 | if DATA_EXTRA_MODULE_URL not in hass.data: 92 | hass.data[DATA_EXTRA_MODULE_URL] = set() 93 | url_set = hass.data[DATA_EXTRA_MODULE_URL] 94 | 95 | for k, v in ICON_FILES.items(): 96 | url_set.remove(ICONS_URL + v) 97 | # if k in modules and modules[k] != False: 98 | url_set.add(ICONS_URL + v) 99 | -------------------------------------------------------------------------------- /custom_components/simpleicons/config_flow.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from homeassistant import config_entries 4 | 5 | _LOGGER = logging.getLogger(__name__) 6 | 7 | 8 | @config_entries.HANDLERS.register("simpleicons") 9 | class simpleiconsConfigFlow(config_entries.ConfigFlow): 10 | async def async_step_user(self, user_input=None): 11 | if self._async_current_entries(): 12 | return self.async_abort(reason="single_instance_allowed") 13 | return self.async_create_entry(title="", data={}) 14 | 15 | async_step_import = async_step_user 16 | -------------------------------------------------------------------------------- /custom_components/simpleicons/data/si.js: -------------------------------------------------------------------------------- 1 | let icons = {}; 2 | 3 | async function getIcon(name) { 4 | let si = undefined; 5 | 6 | if (icons[name] !== undefined) { 7 | si = icons[name]; 8 | } else { 9 | const response = await fetch(`/simpleicons/icons/${name}`); 10 | 11 | if (!response.ok) return {}; 12 | si = await response.json(); 13 | icons[name] = si; 14 | } 15 | 16 | return { 17 | path: si.path, 18 | viewBox: "-1 -1 26 26", 19 | }; 20 | } 21 | 22 | async function getIconList() { 23 | const response = await fetch(`/simpleicons/icons`); 24 | 25 | if (!response.ok) return []; 26 | 27 | const icons = await response.json(); 28 | 29 | return icons; 30 | } 31 | 32 | window.customIcons = window.customIcons || {}; 33 | window.customIcons["si"] = { getIcon, getIconList }; -------------------------------------------------------------------------------- /custom_components/simpleicons/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "simpleicons", 3 | "name": "Simple Icons", 4 | "codeowners": [], 5 | "config_flow": true, 6 | "dependencies": [ 7 | "http", 8 | "frontend" 9 | ], 10 | "documentation": "https://github.com/vigonotion/hass-simpleicons", 11 | "iot_class": "local_polling", 12 | "issue_tracker": "https://github.com/vigonotion/hass-simpleicons/issues", 13 | "requirements": [ 14 | "simplepycons==1!13.17.0" 15 | ], 16 | "version": "0.0.0" 17 | } 18 | -------------------------------------------------------------------------------- /custom_components/simpleicons/translations/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Simple Icons", 3 | "config": { 4 | "abort": { 5 | "single_instance_allowed": "Es ist nur eine Instanz von Simple Icons erlaubt." 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /custom_components/simpleicons/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Simple Icons", 3 | "config": { 4 | "abort": { 5 | "single_instance_allowed": "Only a single configuration of simpleicons is allowed." 6 | }, 7 | "step": {} 8 | } 9 | } -------------------------------------------------------------------------------- /custom_components/simpleicons/translations/nb.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Simple Icons", 3 | "config": { 4 | "abort": { 5 | "single_instance_allowed": "Bare en enkelt konfigurasjon av simpleicons er tillatt." 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /custom_components/simpleicons/translations/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Simple Icons", 3 | "config": { 4 | "abort": { 5 | "single_instance_allowed": "Dozwolona jest tylko jedna konfiguracja Simple Icons." 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simpleicons", 3 | "render_readme": true, 4 | "homeassistant": "2021.11.0", 5 | "zip_release": true, 6 | "filename": "hass-simpleicons.zip" 7 | } -------------------------------------------------------------------------------- /manage/update_manifest.py: -------------------------------------------------------------------------------- 1 | """Update the manifest file.""" 2 | 3 | import json 4 | import os 5 | import sys 6 | 7 | 8 | def update_manifest(): 9 | """Update the manifest file.""" 10 | version = "0.0.0" 11 | 12 | for index, value in enumerate(sys.argv): 13 | if value in ["--version", "-V"]: 14 | version = sys.argv[index + 1] 15 | 16 | with open( 17 | f"{os.getcwd()}/custom_components/simpleicons/manifest.json" 18 | ) as manifestfile: 19 | manifest = json.load(manifestfile) 20 | manifest["version"] = version 21 | 22 | with open( 23 | f"{os.getcwd()}/custom_components/simpleicons/manifest.json", "w" 24 | ) as manifestfile: 25 | manifestfile.write(json.dumps(manifest, indent=4, sort_keys=True)) 26 | 27 | 28 | update_manifest() 29 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | colorlog==6.9.0 2 | homeassistant==2024.6.0 3 | pip>=21.3.1 4 | ruff==0.7.2 5 | -------------------------------------------------------------------------------- /scripts/develop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd "$(dirname "$0")/.." 6 | 7 | # Create config dir if not present 8 | if [[ ! -d "${PWD}/config" ]]; then 9 | mkdir -p "${PWD}/config" 10 | hass --config "${PWD}/config" --script ensure_config 11 | fi 12 | 13 | # Set the path to custom_components 14 | ## This let's us have the structure we want /custom_components/integration_blueprint 15 | ## while at the same time have Home Assistant configuration inside /config 16 | ## without resulting to symlinks. 17 | export PYTHONPATH="${PYTHONPATH}:${PWD}/custom_components" 18 | 19 | # Start Home Assistant 20 | hass --config "${PWD}/config" --debug 21 | -------------------------------------------------------------------------------- /scripts/lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd "$(dirname "$0")/.." 6 | 7 | ruff format . 8 | ruff check . --fix 9 | -------------------------------------------------------------------------------- /scripts/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd "$(dirname "$0")/.." 6 | 7 | python3 -m pip install --requirement requirements.txt 8 | --------------------------------------------------------------------------------