Spring cleaning ☀️ (#23)
* Spring cleaning * Actions * Fix branches * Changes for config_flow
This commit is contained in:
parent
d408fbec4a
commit
a53b0f75a1
@ -1,17 +0,0 @@
|
|||||||
FROM python:3.7
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
git \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN python -m pip install --upgrade colorlog black pylint
|
|
||||||
RUN python -m pip install --upgrade git+https://github.com/home-assistant/home-assistant@dev
|
|
||||||
RUN cd && mkdir -p /config/custom_components
|
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /workspace
|
|
||||||
|
|
||||||
# Set the default shell to bash instead of sh
|
|
||||||
ENV SHELL /bin/bash
|
|
@ -1,53 +0,0 @@
|
|||||||
# Devcontainer
|
|
||||||
|
|
||||||
_The easiest way to contribute to and/or test this repository._
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
|
||||||
- [docker](https://docs.docker.com/install/)
|
|
||||||
- [VS Code](https://code.visualstudio.com/)
|
|
||||||
- [Remote - Containers (VSC Extention)](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
|
||||||
|
|
||||||
[More info about requirements and devcontainer in general](https://code.visualstudio.com/docs/remote/containers#_getting-started)
|
|
||||||
|
|
||||||
## How to use Devcontainer for development/test
|
|
||||||
|
|
||||||
1. Make sure your computer meets the requirements.
|
|
||||||
1. Fork this repository.
|
|
||||||
1. Clone the repository to your computer.
|
|
||||||
1. Open the repository using VS Code.
|
|
||||||
|
|
||||||
When you open this repository with VSCode and your computer meets the requirements you are asked to "Reopen in Container", do that.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
If you don't see this notification, open the command pallet (ctrl+shift+p) and select `Remote-Containers: Reopen Folder in Container`.
|
|
||||||
|
|
||||||
_It will now build the devcontainer._
|
|
||||||
|
|
||||||
The container have some "tasks" to help you testing your changes.
|
|
||||||
|
|
||||||
## Custom Tasks in this repository
|
|
||||||
|
|
||||||
_Start "tasks" by opening the the command pallet (ctrl+shift+p) and select `Tasks: Run Task`_
|
|
||||||
|
|
||||||
Running tasks like `Start Home Assistant on port 8124` can be restarted by opening the the command pallet (ctrl+shift+p) and select `Tasks: Restart Running Task`, then select the task you want to restart.
|
|
||||||
|
|
||||||
### Start Home Assistant on port 8124
|
|
||||||
|
|
||||||
This will copy the configuration and the integration files to the expected location in the container.
|
|
||||||
|
|
||||||
And start up Home Assistant on [port 8124.](http://localhost:8124)
|
|
||||||
|
|
||||||
### Upgrade Home Assistant to latest dev
|
|
||||||
|
|
||||||
This will upgrade Home Assistant to the latest dev version.
|
|
||||||
|
|
||||||
### Set Home Assistant Version
|
|
||||||
|
|
||||||
This allows you to specify a version of Home Assistant to install inside the devcontainer.
|
|
||||||
|
|
||||||
### Home Assistant Config Check
|
|
||||||
|
|
||||||
This runs a config check to make sure your config is valid.
|
|
@ -2,19 +2,4 @@ default_config:
|
|||||||
logger:
|
logger:
|
||||||
default: error
|
default: error
|
||||||
logs:
|
logs:
|
||||||
custom_components.blueprint: debug
|
custom_components.blueprint: debug
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
blueprint:
|
|
||||||
username: my_username
|
|
||||||
password: my_password
|
|
||||||
binary_sensor:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
sensor:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
switch:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
function StartHomeAssistant {
|
|
||||||
echo "Copy configuration.yaml"
|
|
||||||
cp -f .devcontainer/configuration.yaml /config || echo ".devcontainer/configuration.yaml are missing!" exit 1
|
|
||||||
|
|
||||||
echo "Copy the custom component"
|
|
||||||
rm -R /config/custom_components/ || echo ""
|
|
||||||
cp -r custom_components /config/custom_components/ || echo "Could not copy the custom_component" exit 1
|
|
||||||
|
|
||||||
echo "Start Home Assistant"
|
|
||||||
hass -c /config
|
|
||||||
}
|
|
||||||
|
|
||||||
function UpdgradeHomeAssistantDev {
|
|
||||||
python -m pip install --upgrade git+https://github.com/home-assistant/home-assistant@dev
|
|
||||||
}
|
|
||||||
|
|
||||||
function SetHomeAssistantVersion {
|
|
||||||
read -p 'Version: ' version
|
|
||||||
python -m pip install --upgrade homeassistant==$version
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeAssistantConfigCheck {
|
|
||||||
hass -c /config --script check_config
|
|
||||||
}
|
|
@ -1,18 +1,25 @@
|
|||||||
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
|
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
|
||||||
{
|
{
|
||||||
|
"image": "ludeeus/container:integration",
|
||||||
"context": "..",
|
"context": "..",
|
||||||
"dockerFile": "Dockerfile",
|
"appPort": [
|
||||||
"appPort": "8124:8123",
|
"9123:8123"
|
||||||
|
],
|
||||||
|
"postCreateCommand": "dc install",
|
||||||
"runArgs": [
|
"runArgs": [
|
||||||
"-e",
|
"-v",
|
||||||
"GIT_EDTIOR='code --wait'"
|
"${env:HOME}${env:USERPROFILE}/.ssh:/tmp/.ssh"
|
||||||
],
|
],
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"ms-python.python",
|
"ms-python.python",
|
||||||
|
"github.vscode-pull-request-github",
|
||||||
"tabnine.tabnine-vscode"
|
"tabnine.tabnine-vscode"
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"python.pythonPath": "/usr/local/bin/python",
|
"files.eol": "\n",
|
||||||
|
"editor.tabSize": 4,
|
||||||
|
"terminal.integrated.shell.linux": "/bin/bash",
|
||||||
|
"python.pythonPath": "/usr/bin/python3",
|
||||||
"python.linting.pylintEnabled": true,
|
"python.linting.pylintEnabled": true,
|
||||||
"python.linting.enabled": true,
|
"python.linting.enabled": true,
|
||||||
"python.formatting.provider": "black",
|
"python.formatting.provider": "black",
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.0 KiB |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
23
.github/settings.yml
vendored
23
.github/settings.yml
vendored
@ -1,23 +0,0 @@
|
|||||||
repository:
|
|
||||||
private: false
|
|
||||||
has_issues: true
|
|
||||||
has_projects: false
|
|
||||||
has_wiki: false
|
|
||||||
has_downloads: false
|
|
||||||
default_branch: master
|
|
||||||
allow_squash_merge: true
|
|
||||||
allow_merge_commit: false
|
|
||||||
allow_rebase_merge: false
|
|
||||||
labels:
|
|
||||||
- name: "Feature Request"
|
|
||||||
color: "fbca04"
|
|
||||||
- name: "Bug"
|
|
||||||
color: "b60205"
|
|
||||||
- name: "Wont Fix"
|
|
||||||
color: "ffffff"
|
|
||||||
- name: "Enhancement"
|
|
||||||
color: a2eeef
|
|
||||||
- name: "Documentation"
|
|
||||||
color: "008672"
|
|
||||||
- name: "Stale"
|
|
||||||
color: "930191"
|
|
@ -1,14 +1,13 @@
|
|||||||
name: Validate with hassfest
|
name: Cron actions
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate:
|
hassfest:
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
|
name: Validate with hassfest
|
||||||
steps:
|
steps:
|
||||||
- uses: "actions/checkout@v2"
|
- uses: "actions/checkout@v2"
|
||||||
- uses: home-assistant/actions/hassfest@master
|
- uses: "home-assistant/actions/hassfest@master"
|
23
.github/workflows/pull.yml
vendored
Normal file
23
.github/workflows/pull.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: Pull actions
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
hassfest:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
name: Validate with hassfest
|
||||||
|
steps:
|
||||||
|
- uses: "actions/checkout@v2"
|
||||||
|
- uses: "home-assistant/actions/hassfest@master"
|
||||||
|
|
||||||
|
style:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
name: Check style formatting
|
||||||
|
steps:
|
||||||
|
- uses: "actions/checkout@v2"
|
||||||
|
- uses: "actions/setup-python@v1"
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
- run: python3 -m pip install black
|
||||||
|
- run: black .
|
26
.github/workflows/push.yml
vendored
Normal file
26
.github/workflows/push.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
name: Push actions
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
hassfest:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
name: Validate with hassfest
|
||||||
|
steps:
|
||||||
|
- uses: "actions/checkout@v2"
|
||||||
|
- uses: "home-assistant/actions/hassfest@master"
|
||||||
|
|
||||||
|
style:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
name: Check style formatting
|
||||||
|
steps:
|
||||||
|
- uses: "actions/checkout@v2"
|
||||||
|
- uses: "actions/setup-python@v1"
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
- run: python3 -m pip install black
|
||||||
|
- run: black .
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
54
.vscode/tasks.json
vendored
54
.vscode/tasks.json
vendored
@ -2,59 +2,27 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "Start Home Assistant on port 8124",
|
"label": "Run Home Assistant on port 9123",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "source .devcontainer/custom_component_helper && StartHomeAssistant",
|
"command": "dc start",
|
||||||
"group": {
|
"problemMatcher": []
|
||||||
"kind": "test",
|
},
|
||||||
"isDefault": true,
|
{
|
||||||
},
|
"label": "Run Home Assistant configuration against /config",
|
||||||
"presentation": {
|
"type": "shell",
|
||||||
"reveal": "always",
|
"command": "dc check",
|
||||||
"panel": "new"
|
|
||||||
},
|
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Upgrade Home Assistant to latest dev",
|
"label": "Upgrade Home Assistant to latest dev",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "source .devcontainer/custom_component_helper && UpdgradeHomeAssistantDev",
|
"command": "dc install",
|
||||||
"group": {
|
|
||||||
"kind": "test",
|
|
||||||
"isDefault": true,
|
|
||||||
},
|
|
||||||
"presentation": {
|
|
||||||
"reveal": "always",
|
|
||||||
"panel": "new"
|
|
||||||
},
|
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Set Home Assistant Version",
|
"label": "Install a spesific version of Home Assistant",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "source .devcontainer/custom_component_helper && SetHomeAssistantVersion",
|
"command": "dc set-version",
|
||||||
"group": {
|
|
||||||
"kind": "test",
|
|
||||||
"isDefault": true,
|
|
||||||
},
|
|
||||||
"presentation": {
|
|
||||||
"reveal": "always",
|
|
||||||
"panel": "new"
|
|
||||||
},
|
|
||||||
"problemMatcher": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Home Assistant Config Check",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "source .devcontainer/custom_component_helper && HomeAssistantConfigCheck",
|
|
||||||
"group": {
|
|
||||||
"kind": "test",
|
|
||||||
"isDefault": true,
|
|
||||||
},
|
|
||||||
"presentation": {
|
|
||||||
"reveal": "always",
|
|
||||||
"panel": "new"
|
|
||||||
},
|
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 Joakim Sørensen @ludeeus
|
Copyright (c) 2020 Joakim Sørensen @ludeeus
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
53
README.md
53
README.md
@ -79,9 +79,7 @@ Platform | Description
|
|||||||
4. Download _all_ the files from the `custom_components/blueprint/` directory (folder) in this repository.
|
4. Download _all_ the files from the `custom_components/blueprint/` directory (folder) in this repository.
|
||||||
5. Place the files you downloaded in the new directory (folder) you created.
|
5. Place the files you downloaded in the new directory (folder) you created.
|
||||||
6. Restart Home Assistant
|
6. Restart Home Assistant
|
||||||
7. Choose:
|
7. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Blueprint"
|
||||||
- Add `blueprint:` to your HA configuration.
|
|
||||||
- In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Blueprint"
|
|
||||||
|
|
||||||
Using your HA configuration directory (folder) as a starting point you should now also have this:
|
Using your HA configuration directory (folder) as a starting point you should now also have this:
|
||||||
|
|
||||||
@ -98,54 +96,9 @@ custom_components/blueprint/sensor.py
|
|||||||
custom_components/blueprint/switch.py
|
custom_components/blueprint/switch.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example configuration.yaml
|
## Configuration is done in the UI
|
||||||
|
|
||||||
```yaml
|
<!---->
|
||||||
blueprint:
|
|
||||||
username: my_username
|
|
||||||
password: my_password
|
|
||||||
binary_sensor:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
sensor:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
switch:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
Key | Type | Required | Description
|
|
||||||
-- | -- | -- | --
|
|
||||||
`username` | `string` | `False` | Username for the client.
|
|
||||||
`password` | `string` | `False` | Password for the client.
|
|
||||||
`binary_sensor` | `list` | `False` | Configuration for the `binary_sensor` platform.
|
|
||||||
`sensor` | `list` | `False` | Configuration for the `sensor` platform.
|
|
||||||
`switch` | `list` | `False` | Configuration for the `switch` platform.
|
|
||||||
|
|
||||||
### Configuration options for `binary_sensor` list
|
|
||||||
|
|
||||||
Key | Type | Required | Default | Description
|
|
||||||
-- | -- | -- | -- | --
|
|
||||||
`enabled` | `boolean` | `False` | `True` | Boolean to enable/disable the platform.
|
|
||||||
`name` | `string` | `False` | `blueprint` | Custom name for the entity.
|
|
||||||
|
|
||||||
### Configuration options for `sensor` list
|
|
||||||
|
|
||||||
Key | Type | Required | Default | Description
|
|
||||||
-- | -- | -- | -- | --
|
|
||||||
`enabled` | `boolean` | `False` | `True` | Boolean to enable/disable the platform.
|
|
||||||
`name` | `string` | `False` | `blueprint` | Custom name for the entity.
|
|
||||||
|
|
||||||
|
|
||||||
### Configuration options for `switch` list
|
|
||||||
|
|
||||||
Key | Type | Required | Default | Description
|
|
||||||
-- | -- | -- | -- | --
|
|
||||||
`enabled` | `boolean` | `False` | `True` | Boolean to enable/disable the platform.
|
|
||||||
`name` | `string` | `False` | `blueprint` | Custom name for the entity.
|
|
||||||
|
|
||||||
## Contributions are welcome!
|
## Contributions are welcome!
|
||||||
|
|
||||||
|
@ -13,9 +13,17 @@
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"auth": "Username/Password is wrong."
|
"auth": "Username/Password is wrong."
|
||||||
},
|
}
|
||||||
"abort": {
|
},
|
||||||
"single_instance_allowed": "Only a single configuration of Blueprint is allowed."
|
"options": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"binary_sensor": "Binary sensor enabled",
|
||||||
|
"sensor": "Sensor enabled",
|
||||||
|
"switch": "Switch enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,9 +13,17 @@
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"auth": "Brukernavn/Passord er feil."
|
"auth": "Brukernavn/Passord er feil."
|
||||||
},
|
}
|
||||||
"abort": {
|
},
|
||||||
"single_instance_allowed": "Du kan konfigurere Blueprint kun en gang."
|
"options": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"binary_sensor": "Binær sensor aktivert",
|
||||||
|
"sensor": "Sensor aktivert",
|
||||||
|
"switch": "Bryter aktivert"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,244 +1,107 @@
|
|||||||
"""
|
"""
|
||||||
Component to integrate with blueprint.
|
Custom integration to integrate blueprint with Home Assistant.
|
||||||
|
|
||||||
For more details about this component, please refer to
|
For more details about this integration, please refer to
|
||||||
https://github.com/custom-components/blueprint
|
https://github.com/custom-components/blueprint
|
||||||
"""
|
"""
|
||||||
import os
|
import asyncio
|
||||||
from datetime import timedelta
|
|
||||||
import logging
|
import logging
|
||||||
import voluptuous as vol
|
from datetime import timedelta
|
||||||
from homeassistant import config_entries
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.helpers import discovery
|
|
||||||
from homeassistant.util import Throttle
|
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import Config, HomeAssistant
|
||||||
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
from sampleclient.client import Client
|
from sampleclient.client import Client
|
||||||
from integrationhelper.const import CC_STARTUP_VERSION
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_BINARY_SENSOR,
|
|
||||||
CONF_ENABLED,
|
|
||||||
CONF_NAME,
|
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_SENSOR,
|
|
||||||
CONF_SWITCH,
|
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
DEFAULT_NAME,
|
|
||||||
DOMAIN_DATA,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
ISSUE_URL,
|
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
REQUIRED_FILES,
|
STARTUP_MESSAGE,
|
||||||
VERSION,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
|
SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
BINARY_SENSOR_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(CONF_ENABLED, default=True): cv.boolean,
|
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SENSOR_SCHEMA = vol.Schema(
|
async def async_setup(hass: HomeAssistant, config: Config):
|
||||||
{
|
"""Set up this integration using YAML is not supported."""
|
||||||
vol.Optional(CONF_ENABLED, default=True): cv.boolean,
|
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SWITCH_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(CONF_ENABLED, default=True): cv.boolean,
|
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
|
||||||
{
|
|
||||||
DOMAIN: vol.Schema(
|
|
||||||
{
|
|
||||||
vol.Optional(CONF_USERNAME): cv.string,
|
|
||||||
vol.Optional(CONF_PASSWORD): cv.string,
|
|
||||||
vol.Optional(CONF_BINARY_SENSOR): vol.All(
|
|
||||||
cv.ensure_list, [BINARY_SENSOR_SCHEMA]
|
|
||||||
),
|
|
||||||
vol.Optional(CONF_SENSOR): vol.All(cv.ensure_list, [SENSOR_SCHEMA]),
|
|
||||||
vol.Optional(CONF_SWITCH): vol.All(cv.ensure_list, [SWITCH_SCHEMA]),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
},
|
|
||||||
extra=vol.ALLOW_EXTRA,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
|
||||||
"""Set up this component using YAML."""
|
|
||||||
if config.get(DOMAIN) is None:
|
|
||||||
# We get here if the integration is set up using config flow
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Print startup message
|
|
||||||
_LOGGER.info(
|
|
||||||
CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check that all required files are present
|
|
||||||
file_check = await check_files(hass)
|
|
||||||
if not file_check:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Create DATA dict
|
|
||||||
hass.data[DOMAIN_DATA] = {}
|
|
||||||
|
|
||||||
# Get "global" configuration.
|
|
||||||
username = config[DOMAIN].get(CONF_USERNAME)
|
|
||||||
password = config[DOMAIN].get(CONF_PASSWORD)
|
|
||||||
|
|
||||||
# Configure the client.
|
|
||||||
client = Client(username, password)
|
|
||||||
hass.data[DOMAIN_DATA]["client"] = BlueprintData(hass, client)
|
|
||||||
|
|
||||||
# Load platforms
|
|
||||||
for platform in PLATFORMS:
|
|
||||||
# Get platform specific configuration
|
|
||||||
platform_config = config[DOMAIN].get(platform, {})
|
|
||||||
|
|
||||||
# If platform is not enabled, skip.
|
|
||||||
if not platform_config:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for entry in platform_config:
|
|
||||||
entry_config = entry
|
|
||||||
|
|
||||||
# If entry is not enabled, skip.
|
|
||||||
if not entry_config[CONF_ENABLED]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
hass.async_create_task(
|
|
||||||
discovery.async_load_platform(
|
|
||||||
hass, platform, DOMAIN, entry_config, config
|
|
||||||
)
|
|
||||||
)
|
|
||||||
hass.async_create_task(
|
|
||||||
hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry):
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
"""Set up this integration using UI."""
|
"""Set up this integration using UI."""
|
||||||
conf = hass.data.get(DOMAIN_DATA)
|
if hass.data.get(DOMAIN) is None:
|
||||||
if config_entry.source == config_entries.SOURCE_IMPORT:
|
hass.data.setdefault(DOMAIN, {})
|
||||||
if conf is None:
|
_LOGGER.info(STARTUP_MESSAGE)
|
||||||
hass.async_create_task(
|
|
||||||
hass.config_entries.async_remove(config_entry.entry_id)
|
username = entry.data.get(CONF_USERNAME)
|
||||||
|
password = entry.data.get(CONF_PASSWORD)
|
||||||
|
|
||||||
|
coordinator = BlueprintDataUpdateCoordinator(
|
||||||
|
hass, username=username, password=password
|
||||||
|
)
|
||||||
|
await coordinator.async_refresh()
|
||||||
|
|
||||||
|
if not coordinator.last_update_success:
|
||||||
|
raise ConfigEntryNotReady
|
||||||
|
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||||
|
|
||||||
|
for platform in PLATFORMS:
|
||||||
|
if entry.options.get(platform, True):
|
||||||
|
coordinator.platforms.append(platform)
|
||||||
|
hass.async_add_job(
|
||||||
|
hass.config_entries.async_forward_entry_setup(entry, platform)
|
||||||
)
|
)
|
||||||
return False
|
|
||||||
|
|
||||||
# Print startup message
|
|
||||||
_LOGGER.info(
|
|
||||||
CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check that all required files are present
|
|
||||||
file_check = await check_files(hass)
|
|
||||||
if not file_check:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Create DATA dict
|
|
||||||
hass.data[DOMAIN_DATA] = {}
|
|
||||||
|
|
||||||
# Get "global" configuration.
|
|
||||||
username = config_entry.data.get(CONF_USERNAME)
|
|
||||||
password = config_entry.data.get(CONF_PASSWORD)
|
|
||||||
|
|
||||||
# Configure the client.
|
|
||||||
client = Client(username, password)
|
|
||||||
hass.data[DOMAIN_DATA]["client"] = BlueprintData(hass, client)
|
|
||||||
|
|
||||||
# Add binary_sensor
|
|
||||||
hass.async_add_job(
|
|
||||||
hass.config_entries.async_forward_entry_setup(config_entry, "binary_sensor")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add sensor
|
|
||||||
hass.async_add_job(
|
|
||||||
hass.config_entries.async_forward_entry_setup(config_entry, "sensor")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add switch
|
|
||||||
hass.async_add_job(
|
|
||||||
hass.config_entries.async_forward_entry_setup(config_entry, "switch")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
entry.add_update_listener(async_reload_entry)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class BlueprintData:
|
class BlueprintDataUpdateCoordinator(DataUpdateCoordinator):
|
||||||
"""This class handle communication and stores the data."""
|
"""Class to manage fetching data from the API."""
|
||||||
|
|
||||||
def __init__(self, hass, client):
|
def __init__(self, hass, username, password):
|
||||||
"""Initialize the class."""
|
"""Initialize."""
|
||||||
self.hass = hass
|
self.api = Client(username, password)
|
||||||
self.client = client
|
self.platforms = []
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
super().__init__(
|
||||||
async def update_data(self):
|
hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL,
|
||||||
"""Update data."""
|
)
|
||||||
# This is where the main logic to update platform data goes.
|
|
||||||
|
async def _async_update_data(self):
|
||||||
|
"""Update data via library."""
|
||||||
try:
|
try:
|
||||||
data = self.client.get_data()
|
data = await self.api.async_get_data()
|
||||||
self.hass.data[DOMAIN_DATA]["data"] = data
|
return data.get("data", {})
|
||||||
except Exception as error: # pylint: disable=broad-except
|
except Exception as exception:
|
||||||
_LOGGER.error("Could not update data - %s", error)
|
raise UpdateFailed(exception)
|
||||||
|
|
||||||
|
|
||||||
async def check_files(hass):
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
"""Return bool that indicates if all files are present."""
|
|
||||||
# Verify that the user downloaded all files.
|
|
||||||
base = f"{hass.config.path()}/custom_components/{DOMAIN}/"
|
|
||||||
missing = []
|
|
||||||
for file in REQUIRED_FILES:
|
|
||||||
fullpath = "{}{}".format(base, file)
|
|
||||||
if not os.path.exists(fullpath):
|
|
||||||
missing.append(file)
|
|
||||||
|
|
||||||
if missing:
|
|
||||||
_LOGGER.critical("The following files are missing: %s", str(missing))
|
|
||||||
returnvalue = False
|
|
||||||
else:
|
|
||||||
returnvalue = True
|
|
||||||
|
|
||||||
return returnvalue
|
|
||||||
|
|
||||||
|
|
||||||
async def async_remove_entry(hass, config_entry):
|
|
||||||
"""Handle removal of an entry."""
|
"""Handle removal of an entry."""
|
||||||
try:
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
await hass.config_entries.async_forward_entry_unload(
|
unloaded = all(
|
||||||
config_entry, "binary_sensor"
|
await asyncio.gather(
|
||||||
|
*[
|
||||||
|
hass.config_entries.async_forward_entry_unload(entry, platform)
|
||||||
|
for platform in PLATFORMS
|
||||||
|
if platform in coordinator.platforms
|
||||||
|
]
|
||||||
)
|
)
|
||||||
_LOGGER.info(
|
)
|
||||||
"Successfully removed binary_sensor from the blueprint integration"
|
if unloaded:
|
||||||
)
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
return unloaded
|
||||||
await hass.config_entries.async_forward_entry_unload(config_entry, "sensor")
|
|
||||||
_LOGGER.info("Successfully removed sensor from the blueprint integration")
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
await hass.config_entries.async_forward_entry_unload(config_entry, "switch")
|
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
_LOGGER.info("Successfully removed switch from the blueprint integration")
|
"""Reload config entry."""
|
||||||
except ValueError:
|
await async_unload_entry(hass, entry)
|
||||||
pass
|
await async_setup_entry(hass, entry)
|
||||||
|
@ -1,73 +1,28 @@
|
|||||||
"""Binary sensor platform for blueprint."""
|
"""Binary sensor platform for blueprint."""
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||||
from .const import (
|
|
||||||
ATTRIBUTION,
|
from custom_components.blueprint.const import (
|
||||||
|
BINARY_SENSOR,
|
||||||
BINARY_SENSOR_DEVICE_CLASS,
|
BINARY_SENSOR_DEVICE_CLASS,
|
||||||
DEFAULT_NAME,
|
DEFAULT_NAME,
|
||||||
DOMAIN_DATA,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
from custom_components.blueprint.entity import BlueprintEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_entry(hass, entry, async_add_devices):
|
||||||
hass, config, async_add_entities, discovery_info=None
|
|
||||||
): # pylint: disable=unused-argument
|
|
||||||
"""Setup binary_sensor platform."""
|
"""Setup binary_sensor platform."""
|
||||||
async_add_entities([BlueprintBinarySensor(hass, discovery_info)], True)
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
async_add_devices([BlueprintBinarySensor(coordinator, entry)])
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_devices):
|
class BlueprintBinarySensor(BlueprintEntity, BinarySensorDevice):
|
||||||
"""Setup sensor platform."""
|
|
||||||
async_add_devices([BlueprintBinarySensor(hass, {})], True)
|
|
||||||
|
|
||||||
|
|
||||||
class BlueprintBinarySensor(BinarySensorDevice):
|
|
||||||
"""blueprint binary_sensor class."""
|
"""blueprint binary_sensor class."""
|
||||||
|
|
||||||
def __init__(self, hass, config):
|
|
||||||
self.hass = hass
|
|
||||||
self.attr = {}
|
|
||||||
self._status = False
|
|
||||||
self._name = config.get("name", DEFAULT_NAME)
|
|
||||||
|
|
||||||
async def async_update(self):
|
|
||||||
"""Update the binary_sensor."""
|
|
||||||
# Send update "signal" to the component
|
|
||||||
await self.hass.data[DOMAIN_DATA]["client"].update_data()
|
|
||||||
|
|
||||||
# Get new data (if any)
|
|
||||||
updated = self.hass.data[DOMAIN_DATA]["data"].get("data", {})
|
|
||||||
|
|
||||||
# Check the data and update the value.
|
|
||||||
if updated.get("bool_on") is None:
|
|
||||||
self._status = self._status
|
|
||||||
else:
|
|
||||||
self._status = updated.get("bool_on")
|
|
||||||
|
|
||||||
# Set/update attributes
|
|
||||||
self.attr["attribution"] = ATTRIBUTION
|
|
||||||
self.attr["time"] = str(updated.get("time"))
|
|
||||||
self.attr["static"] = updated.get("static")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self):
|
|
||||||
"""Return a unique ID to use for this binary_sensor."""
|
|
||||||
return (
|
|
||||||
"0919a0cd-745c-48fd"
|
|
||||||
) # Don't hard code this, use something from the device/service.
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self):
|
|
||||||
return {
|
|
||||||
"identifiers": {(DOMAIN, self.unique_id)},
|
|
||||||
"name": self.name,
|
|
||||||
"manufacturer": "Blueprint",
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the binary_sensor."""
|
"""Return the name of the binary_sensor."""
|
||||||
return self._name
|
return f"{DEFAULT_NAME}_{BINARY_SENSOR}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
@ -77,9 +32,4 @@ class BlueprintBinarySensor(BinarySensorDevice):
|
|||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if the binary_sensor is on."""
|
"""Return true if the binary_sensor is on."""
|
||||||
return self._status
|
return self.coordinator.data.get("bool_on", False)
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
return self.attr
|
|
||||||
|
@ -4,12 +4,17 @@ from collections import OrderedDict
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from sampleclient.client import Client
|
from sampleclient.client import Client
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from .const import DOMAIN
|
from custom_components.blueprint.const import (
|
||||||
|
DOMAIN,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_USERNAME,
|
||||||
|
PLATFORMS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@config_entries.HANDLERS.register(DOMAIN)
|
class BlueprintFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
class BlueprintFlowHandler(config_entries.ConfigFlow):
|
|
||||||
"""Config flow for Blueprint."""
|
"""Config flow for Blueprint."""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
@ -24,17 +29,14 @@ class BlueprintFlowHandler(config_entries.ConfigFlow):
|
|||||||
): # pylint: disable=dangerous-default-value
|
): # pylint: disable=dangerous-default-value
|
||||||
"""Handle a flow initialized by the user."""
|
"""Handle a flow initialized by the user."""
|
||||||
self._errors = {}
|
self._errors = {}
|
||||||
if self._async_current_entries():
|
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
|
||||||
if self.hass.data.get(DOMAIN):
|
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
valid = await self._test_credentials(
|
valid = await self._test_credentials(
|
||||||
user_input["username"], user_input["password"]
|
user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
|
||||||
)
|
)
|
||||||
if valid:
|
if valid:
|
||||||
return self.async_create_entry(title="", data=user_input)
|
return self.async_create_entry(
|
||||||
|
title=user_input[CONF_USERNAME], data=user_input
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._errors["base"] = "auth"
|
self._errors["base"] = "auth"
|
||||||
|
|
||||||
@ -42,42 +44,62 @@ class BlueprintFlowHandler(config_entries.ConfigFlow):
|
|||||||
|
|
||||||
return await self._show_config_form(user_input)
|
return await self._show_config_form(user_input)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@callback
|
||||||
|
def async_get_options_flow(config_entry):
|
||||||
|
return BlueprintOptionsFlowHandler(config_entry)
|
||||||
|
|
||||||
async def _show_config_form(self, user_input):
|
async def _show_config_form(self, user_input):
|
||||||
"""Show the configuration form to edit location data."""
|
"""Show the configuration form to edit location data."""
|
||||||
|
|
||||||
# Defaults
|
|
||||||
username = ""
|
|
||||||
password = ""
|
|
||||||
|
|
||||||
if user_input is not None:
|
|
||||||
if "username" in user_input:
|
|
||||||
username = user_input["username"]
|
|
||||||
if "password" in user_input:
|
|
||||||
password = user_input["password"]
|
|
||||||
|
|
||||||
data_schema = OrderedDict()
|
|
||||||
data_schema[vol.Required("username", default=username)] = str
|
|
||||||
data_schema[vol.Required("password", default=password)] = str
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user", data_schema=vol.Schema(data_schema), errors=self._errors
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str,}
|
||||||
|
),
|
||||||
|
errors=self._errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_import(self, user_input): # pylint: disable=unused-argument
|
|
||||||
"""Import a config entry.
|
|
||||||
Special type of import, we're not actually going to store any data.
|
|
||||||
Instead, we're going to rely on the values that are in config file.
|
|
||||||
"""
|
|
||||||
if self._async_current_entries():
|
|
||||||
return self.async_abort(reason="single_instance_allowed")
|
|
||||||
|
|
||||||
return self.async_create_entry(title="configuration.yaml", data={})
|
|
||||||
|
|
||||||
async def _test_credentials(self, username, password):
|
async def _test_credentials(self, username, password):
|
||||||
"""Return true if credentials is valid."""
|
"""Return true if credentials is valid."""
|
||||||
try:
|
try:
|
||||||
client = Client(username, password)
|
client = Client(username, password)
|
||||||
client.get_data()
|
await client.async_get_data()
|
||||||
return True
|
return True
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class BlueprintOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
|
"""Blueprint config flow options handler."""
|
||||||
|
|
||||||
|
def __init__(self, config_entry):
|
||||||
|
"""Initialize HACS options flow."""
|
||||||
|
self.config_entry = config_entry
|
||||||
|
self.options = dict(config_entry.options)
|
||||||
|
|
||||||
|
async def async_step_init(self, user_input=None):
|
||||||
|
"""Manage the options."""
|
||||||
|
return await self.async_step_user()
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Handle a flow initialized by the user."""
|
||||||
|
if user_input is not None:
|
||||||
|
self.options.update(user_input)
|
||||||
|
return await self._update_options()
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(x, default=self.options.get(x, True)): bool
|
||||||
|
for x in sorted(PLATFORMS)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _update_options(self):
|
||||||
|
"""Update config entry options."""
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=self.config_entry.data.get(CONF_USERNAME), data=self.options
|
||||||
|
)
|
||||||
|
@ -1,20 +1,11 @@
|
|||||||
"""Constants for blueprint."""
|
"""Constants for blueprint."""
|
||||||
# Base component constants
|
# Base component constants
|
||||||
|
NAME = "Blueprint"
|
||||||
DOMAIN = "blueprint"
|
DOMAIN = "blueprint"
|
||||||
DOMAIN_DATA = f"{DOMAIN}_data"
|
DOMAIN_DATA = f"{DOMAIN}_data"
|
||||||
VERSION = "0.0.1"
|
VERSION = "0.0.1"
|
||||||
PLATFORMS = ["binary_sensor", "sensor", "switch"]
|
|
||||||
REQUIRED_FILES = [
|
|
||||||
".translations/en.json",
|
|
||||||
"binary_sensor.py",
|
|
||||||
"const.py",
|
|
||||||
"config_flow.py",
|
|
||||||
"manifest.json",
|
|
||||||
"sensor.py",
|
|
||||||
"switch.py",
|
|
||||||
]
|
|
||||||
ISSUE_URL = "https://github.com/custom-components/blueprint/issues"
|
ISSUE_URL = "https://github.com/custom-components/blueprint/issues"
|
||||||
ATTRIBUTION = "Data from this is provided by blueprint."
|
|
||||||
|
|
||||||
# Icons
|
# Icons
|
||||||
ICON = "mdi:format-quote-close"
|
ICON = "mdi:format-quote-close"
|
||||||
@ -22,14 +13,28 @@ ICON = "mdi:format-quote-close"
|
|||||||
# Device classes
|
# Device classes
|
||||||
BINARY_SENSOR_DEVICE_CLASS = "connectivity"
|
BINARY_SENSOR_DEVICE_CLASS = "connectivity"
|
||||||
|
|
||||||
# Configuration
|
# Platforms
|
||||||
CONF_BINARY_SENSOR = "binary_sensor"
|
BINARY_SENSOR = "binary_sensor"
|
||||||
CONF_SENSOR = "sensor"
|
SENSOR = "sensor"
|
||||||
CONF_SWITCH = "switch"
|
SWITCH = "switch"
|
||||||
|
PLATFORMS = [BINARY_SENSOR, SENSOR, SWITCH]
|
||||||
|
|
||||||
|
|
||||||
|
# Configuration and options
|
||||||
CONF_ENABLED = "enabled"
|
CONF_ENABLED = "enabled"
|
||||||
CONF_NAME = "name"
|
|
||||||
CONF_USERNAME = "username"
|
CONF_USERNAME = "username"
|
||||||
CONF_PASSWORD = "password"
|
CONF_PASSWORD = "password"
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
DEFAULT_NAME = DOMAIN
|
DEFAULT_NAME = DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
STARTUP_MESSAGE = f"""
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
{NAME}
|
||||||
|
Version: {VERSION}
|
||||||
|
This is a custom integration!
|
||||||
|
If you have any issues with this you need to open an issue here:
|
||||||
|
{ISSUE_URL}
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
"""
|
||||||
|
54
custom_components/blueprint/entity.py
Normal file
54
custom_components/blueprint/entity.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"""BlueprintEntity class"""
|
||||||
|
from homeassistant.helpers import entity
|
||||||
|
|
||||||
|
from custom_components.blueprint.const import DOMAIN, VERSION, NAME
|
||||||
|
|
||||||
|
|
||||||
|
class BlueprintEntity(entity.Entity):
|
||||||
|
def __init__(self, coordinator, config_entry):
|
||||||
|
self.coordinator = coordinator
|
||||||
|
self.config_entry = config_entry
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
"""No need to poll. Coordinator notifies entity of updates."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
"""Return if entity is available."""
|
||||||
|
return self.coordinator.last_update_success
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return a unique ID to use for this entity."""
|
||||||
|
return self.config_entry.entry_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
return {
|
||||||
|
"identifiers": {(DOMAIN, self.unique_id)},
|
||||||
|
"name": NAME,
|
||||||
|
"model": VERSION,
|
||||||
|
"manufacturer": NAME,
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
return {
|
||||||
|
"time": str(self.coordinator.data.get("time")),
|
||||||
|
"static": self.coordinator.data.get("static"),
|
||||||
|
}
|
||||||
|
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""Connect to dispatcher listening for entity data notifications."""
|
||||||
|
self.coordinator.async_add_listener(self.async_write_ha_state)
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self):
|
||||||
|
"""Disconnect from update signal."""
|
||||||
|
self.coordinator.async_remove_listener(self.async_write_ha_state)
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Update Brother entity."""
|
||||||
|
await self.coordinator.async_request_refresh()
|
@ -8,7 +8,6 @@
|
|||||||
"@ludeeus"
|
"@ludeeus"
|
||||||
],
|
],
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"sampleclient",
|
"sampleclient"
|
||||||
"integrationhelper"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,79 +1,28 @@
|
|||||||
"""Sensor platform for blueprint."""
|
"""Sensor platform for blueprint."""
|
||||||
from homeassistant.helpers.entity import Entity
|
from custom_components.blueprint.const import DEFAULT_NAME, DOMAIN, ICON, SENSOR
|
||||||
from .const import ATTRIBUTION, DEFAULT_NAME, DOMAIN_DATA, ICON, DOMAIN
|
from custom_components.blueprint.entity import BlueprintEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_entry(hass, entry, async_add_devices):
|
||||||
hass, config, async_add_entities, discovery_info=None
|
|
||||||
): # pylint: disable=unused-argument
|
|
||||||
"""Setup sensor platform."""
|
"""Setup sensor platform."""
|
||||||
async_add_entities([BlueprintSensor(hass, discovery_info)], True)
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
async_add_devices([BlueprintSensor(coordinator, entry)])
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_devices):
|
class BlueprintSensor(BlueprintEntity):
|
||||||
"""Setup sensor platform."""
|
|
||||||
async_add_devices([BlueprintSensor(hass, {})], True)
|
|
||||||
|
|
||||||
|
|
||||||
class BlueprintSensor(Entity):
|
|
||||||
"""blueprint Sensor class."""
|
"""blueprint Sensor class."""
|
||||||
|
|
||||||
def __init__(self, hass, config):
|
|
||||||
self.hass = hass
|
|
||||||
self.attr = {}
|
|
||||||
self._state = None
|
|
||||||
self._name = config.get("name", DEFAULT_NAME)
|
|
||||||
|
|
||||||
async def async_update(self):
|
|
||||||
"""Update the sensor."""
|
|
||||||
# Send update "signal" to the component
|
|
||||||
await self.hass.data[DOMAIN_DATA]["client"].update_data()
|
|
||||||
|
|
||||||
# Get new data (if any)
|
|
||||||
updated = self.hass.data[DOMAIN_DATA]["data"].get("data", {})
|
|
||||||
|
|
||||||
# Check the data and update the value.
|
|
||||||
if updated.get("static") is None:
|
|
||||||
self._state = self._state
|
|
||||||
else:
|
|
||||||
self._state = updated.get("static")
|
|
||||||
|
|
||||||
# Set/update attributes
|
|
||||||
self.attr["attribution"] = ATTRIBUTION
|
|
||||||
self.attr["time"] = str(updated.get("time"))
|
|
||||||
self.attr["none"] = updated.get("none")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self):
|
|
||||||
"""Return a unique ID to use for this sensor."""
|
|
||||||
return (
|
|
||||||
"0717a0cd-745c-48fd"
|
|
||||||
) # Don't hard code this, use something from the device/service.
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self):
|
|
||||||
return {
|
|
||||||
"identifiers": {(DOMAIN, self.unique_id)},
|
|
||||||
"name": self.name,
|
|
||||||
"manufacturer": "Blueprint",
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the sensor."""
|
"""Return the name of the sensor."""
|
||||||
return self._name
|
return f"{DEFAULT_NAME}_{SENSOR}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return self._state
|
return self.coordinator.data.get("static")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
"""Return the icon of the sensor."""
|
"""Return the icon of the sensor."""
|
||||||
return ICON
|
return ICON
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
return self.attr
|
|
||||||
|
@ -1,72 +1,34 @@
|
|||||||
"""Switch platform for blueprint."""
|
"""Switch platform for blueprint."""
|
||||||
from homeassistant.components.switch import SwitchDevice
|
from homeassistant.components.switch import SwitchDevice
|
||||||
from .const import ATTRIBUTION, DEFAULT_NAME, DOMAIN_DATA, ICON, DOMAIN
|
|
||||||
|
from custom_components.blueprint.const import DEFAULT_NAME, DOMAIN, ICON, SWITCH
|
||||||
|
|
||||||
|
from custom_components.blueprint.entity import BlueprintEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_entry(hass, entry, async_add_devices):
|
||||||
hass, config, async_add_entities, discovery_info=None
|
|
||||||
): # pylint: disable=unused-argument
|
|
||||||
"""Setup switch platform."""
|
|
||||||
async_add_entities([BlueprintBinarySwitch(hass, discovery_info)], True)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_devices):
|
|
||||||
"""Setup sensor platform."""
|
"""Setup sensor platform."""
|
||||||
async_add_devices([BlueprintBinarySwitch(hass, {})], True)
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
async_add_devices([BlueprintBinarySwitch(coordinator, entry)])
|
||||||
|
|
||||||
|
|
||||||
class BlueprintBinarySwitch(SwitchDevice):
|
class BlueprintBinarySwitch(BlueprintEntity, SwitchDevice):
|
||||||
"""blueprint switch class."""
|
"""blueprint switch class."""
|
||||||
|
|
||||||
def __init__(self, hass, config):
|
|
||||||
self.hass = hass
|
|
||||||
self.attr = {}
|
|
||||||
self._status = False
|
|
||||||
self._name = config.get("name", DEFAULT_NAME)
|
|
||||||
|
|
||||||
async def async_update(self):
|
|
||||||
"""Update the switch."""
|
|
||||||
# Send update "signal" to the component
|
|
||||||
await self.hass.data[DOMAIN_DATA]["client"].update_data()
|
|
||||||
|
|
||||||
# Get new data (if any)
|
|
||||||
updated = self.hass.data[DOMAIN_DATA]["data"].get("data", {})
|
|
||||||
|
|
||||||
# Check the data and update the value.
|
|
||||||
self._status = self.hass.data[DOMAIN_DATA]["client"].client.something
|
|
||||||
|
|
||||||
# Set/update attributes
|
|
||||||
self.attr["attribution"] = ATTRIBUTION
|
|
||||||
self.attr["time"] = str(updated.get("time"))
|
|
||||||
self.attr["static"] = updated.get("static")
|
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument
|
async def async_turn_on(self, **kwargs): # pylint: disable=unused-argument
|
||||||
"""Turn on the switch."""
|
"""Turn on the switch."""
|
||||||
await self.hass.data[DOMAIN_DATA]["client"].client.change_something(True)
|
await self.coordinator.api.async_change_something(True)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument
|
async def async_turn_off(self, **kwargs): # pylint: disable=unused-argument
|
||||||
"""Turn off the switch."""
|
"""Turn off the switch."""
|
||||||
await self.hass.data[DOMAIN_DATA]["client"].client.change_something(False)
|
await self.coordinator.api.async_change_something(False)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
@property
|
|
||||||
def unique_id(self):
|
|
||||||
"""Return a unique ID to use for this switch."""
|
|
||||||
return (
|
|
||||||
"0818a0cd-745c-48fd"
|
|
||||||
) # Don't hard code this, use something from the device/service.
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_info(self):
|
|
||||||
return {
|
|
||||||
"identifiers": {(DOMAIN, self.unique_id)},
|
|
||||||
"name": self.name,
|
|
||||||
"manufacturer": "Blueprint",
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the switch."""
|
"""Return the name of the switch."""
|
||||||
return self._name
|
return f"{DEFAULT_NAME}_{SWITCH}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
@ -76,9 +38,4 @@ class BlueprintBinarySwitch(SwitchDevice):
|
|||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if the switch is on."""
|
"""Return true if the switch is on."""
|
||||||
return self._status
|
return self.coordinator.api.something
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return the state attributes."""
|
|
||||||
return self.attr
|
|
||||||
|
5
hacs.json
Normal file
5
hacs.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Blueprint",
|
||||||
|
"hacs": "0.19.0",
|
||||||
|
"homeassistant": "0.97.0"
|
||||||
|
}
|
50
info.md
50
info.md
@ -25,58 +25,14 @@ Platform | Description
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Click install.
|
1. Click install.
|
||||||
1. Add `blueprint:` to your HA configuration.
|
1. In the HA UI go to "Configuration" -> "Integrations" click "+" and search for "Blueprint".
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
## Example configuration.yaml
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
blueprint:
|
|
||||||
username: my_username
|
|
||||||
password: my_password
|
|
||||||
binary_sensor:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
sensor:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
switch:
|
|
||||||
- enabled: true
|
|
||||||
name: My custom name
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
Key | Type | Required | Description
|
|
||||||
-- | -- | -- | --
|
|
||||||
`username` | `string` | `False` | Username for the client.
|
|
||||||
`password` | `string` | `False` | Password for the client.
|
|
||||||
`binary_sensor` | `list` | `False` | Configuration for the `binary_sensor` platform.
|
|
||||||
`sensor` | `list` | `False` | Configuration for the `sensor` platform.
|
|
||||||
`switch` | `list` | `False` | Configuration for the `switch` platform.
|
|
||||||
|
|
||||||
### Configuration options for `binary_sensor` list
|
|
||||||
|
|
||||||
Key | Type | Required | Default | Description
|
|
||||||
-- | -- | -- | -- | --
|
|
||||||
`enabled` | `boolean` | `False` | `True` | Boolean to enable/disable the platform.
|
|
||||||
`name` | `string` | `False` | `blueprint` | Custom name for the entity.
|
|
||||||
|
|
||||||
### Configuration options for `sensor` list
|
|
||||||
|
|
||||||
Key | Type | Required | Default | Description
|
|
||||||
-- | -- | -- | -- | --
|
|
||||||
`enabled` | `boolean` | `False` | `True` | Boolean to enable/disable the platform.
|
|
||||||
`name` | `string` | `False` | `blueprint` | Custom name for the entity.
|
|
||||||
|
|
||||||
|
|
||||||
### Configuration options for `switch` list
|
## Configuration is done in the UI
|
||||||
|
|
||||||
Key | Type | Required | Default | Description
|
|
||||||
-- | -- | -- | -- | --
|
|
||||||
`enabled` | `boolean` | `False` | `True` | Boolean to enable/disable the platform.
|
|
||||||
`name` | `string` | `False` | `blueprint` | Custom name for the entity.
|
|
||||||
|
|
||||||
|
<!---->
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
integrationhelper
|
|
||||||
sampleclient
|
|
Loading…
x
Reference in New Issue
Block a user