├── README.md
├── apps
└── check-config
│ └── checkconfig.py
├── confignotify.py
├── info.md
├── lovelace-example.png
├── picture-elements-example.jpeg
├── result-error.png
└── telegram.png
/README.md:
--------------------------------------------------------------------------------
1 | # Check Config
2 | [](https://github.com/custom-components/hacs)
3 |
9 |
10 | ### Requires AppDaemon
11 |
12 | **As of version 2.0, I am only testing against AppDaemon 4.x. The app will likely continue to work with
13 | AppDaemon 3.x, but if you have an issue and are using AppDaemon 3.x, I will not be able to provide support.**
14 |
15 | _Allows you to check your Home Assistant configuration from within Lovelace, or
16 | automatically using when configuration.yaml is updated.
17 | Outputs the results to a sensor and can automatically restart HASS if
18 | configuration is valid._
19 |
20 | ## Installation
21 |
22 | This app is best installed using
23 | [HACS](https://github.com/custom-components/hacs), so that you can easily track
24 | and download updates.
25 |
26 | Alternatively, you can download the `check-config` directory from inside the `apps` directory here to your
27 | local `apps` directory, then add the configuration to enable the `checkconfig`
28 | module.
29 |
30 | ## How it works
31 |
32 | The app will auto-generate an entity called `sensor.config_result`. It has an
33 | initial value of `-` until a configuration check is actually run. After running
34 | a check, it will change to `valid` or `invalid`. If it is invalid, the `detail`
35 | attribute on the sensor will include additional data about the specific failure.
36 | If it is valid, Home Assistant can restart automatically, or you can configure
37 | it to not restart until you do so manually.
38 |
39 | ## App configuration
40 |
41 | Add the following to your `apps.yaml` file:
42 | ```yaml
43 | check_config:
44 | module: checkconfig
45 | class: CheckConfig
46 | ```
47 |
48 | key | optional | type | default | description
49 | -- | -- | -- | -- | --
50 | `module` | False | string | | `checkconfig`
51 | `class` | False | string | | `CheckConfig`
52 | `restart` | True | boolean | True | By default, Home Assistant will be restarted if the configuration is valid. Setting this to false will allow you to restart at the time of your choosing.
53 | `folder_watcher` | True | boolean | False | If you are using the `folder_watcher` component (see "Using with Folder Watcher" below), the app can automatically monitor for changes to `configuration.yaml` and run the check any time the file is changed.
54 |
55 | ## Using With Lovelace
56 |
57 | You will need to create an entity called `script.check_config`, but this script
58 | can be blank and won't actually perform any steps. It simply allows us to call
59 | the config check from Lovelace. Copying and pasting the following into your
60 | `scripts.yaml` file will add the proper script.
61 |
62 | ```yaml
63 | check_config:
64 | sequence: []
65 | alias: Check Configuration
66 | ```
67 |
68 | The following is a basic example of a Lovelace example to run the check and show
69 | the results.
70 |
71 | ```yaml
72 | type: entities
73 | title: Check Configuration
74 | show_header_toggle: false
75 | entities:
76 | - script.check_config
77 | - sensor.config_result
78 | ```
79 |
80 | ## Using with Folder Watcher
81 | _Note: Folder Watcher support currently does not support split configuration files, nor will it monitor changes to secrets.yaml. Support for secrets.yaml and split configurations is planned for the next release. In addition, when using the Folder Watcher configuration, the Lovelace script configuration above will no longer work (but the sensor will still update for you to optionally display in Lovelace)._
82 |
83 | As of version 0.3.0, the [Folder
84 | Watcher](https://www.home-assistant.io/components/folder_watcher/) component can
85 | be integrated into this app. The app will then automatically trigger a
86 | configuration check when your `configuration.yaml` file is updated. You can then
87 | either have Home Assistant restart automatically, or continue to utilize the
88 | sensor updating as you see fit. For example, I have Telegram set up on the
89 | computer I perform my configuration updates on. So, I now receive a notification
90 | from Telegram on my desktop seconds after saving a change to
91 | `configuration.yaml`. If the config is invalid, the specific issue is called out
92 | in the notification. Otherwise, if it's valid, I have an inline keyboard button
93 | that will restart Home Assistant when pressed.
94 |
95 | The example code for this notification setup is included in `confignotify.py` in
96 | this repository. Since everyone's notification setup is different, I am not
97 | attempting to include it in the main app at this time, but you are free to use
98 | what I have created as a starting point.
99 |
100 |
102 |
103 | Here is the YAML I'm using to include `folder_watcher` in my setup to monitor
104 | `configuration.yaml`:
105 | ```yaml
106 | folder_watcher:
107 | - folder: /config
108 | patterns:
109 | - '*.yaml'
110 | ```
111 |
112 | You also need your `config` directory under `whitelist_external_dirs` in the
113 | `homeassistant` section of `configuration.yaml`:
114 | ```yaml
115 | homeassistant:
116 | whitelist_external_dirs:
117 | - /config
118 | ```
119 |
120 | ## Example Screenshots
121 |
122 |
124 |
125 | ## Advanced Lovelace Config Examples
126 |
127 | As a picture elements card with options to reload elements of your
128 | configuration, or a full restart:
129 | ```yaml
130 | type: custom:vertical-stack-in-card
131 | cards:
132 | - type: picture-elements
133 | image: /local/images/BG_blank_slim.png
134 | elements:
135 |
136 | - type: image
137 | image: /local/images/icons8-administrative-tools-80.png
138 | tap_action:
139 | action: call-service
140 | service: script.check_config
141 | style:
142 | top: 50%
143 | left: 10%
144 | width: 10%
145 |
146 | - type: image
147 | image: /local/images/icons8-restart-80.png
148 | tap_action:
149 | action: call-service
150 | service: homeassistant.restart
151 | style:
152 | top: 50%
153 | left: 26%
154 | width: 10%
155 |
156 | - type: image
157 | image: /local/images/icons8-source-code-80.png
158 | tap_action:
159 | action: call-service
160 | service: homeassistant.reload_core_config
161 | style:
162 | top: 50%
163 | left: 42%
164 | width: 10%
165 |
166 | - type: image
167 | image: /local/images/icons8-variation-80.png
168 | tap_action:
169 | action: call-service
170 | service: group.reload
171 | style:
172 | top: 50%
173 | left: 58%
174 | width: 10%
175 |
176 | - type: image
177 | image: /local/images/icons8-automation-80.png
178 | tap_action:
179 | action: call-service
180 | service: automation.reload
181 | style:
182 | top: 50%
183 | left: 74%
184 | width: 10%
185 |
186 | - type: image
187 | image: /local/images/icons8-note-80.png
188 | tap_action:
189 | action: call-service
190 | service: script.reload
191 | style:
192 | top: 50%
193 | left: 90%
194 | width: 10%
195 |
196 |
197 | - type: entities
198 | show_header_toggle: false
199 | entities:
200 | - sensor.config_result
201 | ```
202 |
203 |
204 | ## Issues/Feature Requests
205 |
206 | Please log any issues or feature requests in this GitHub repository for me to review.
207 |
--------------------------------------------------------------------------------
/apps/check-config/checkconfig.py:
--------------------------------------------------------------------------------
1 | import appdaemon.plugins.hass.hassapi as hass
2 | import requests
3 | import json
4 |
5 |
6 | # Check Home Assistant Configuration
7 |
8 | class CheckConfig(hass.Hass):
9 |
10 | def initialize(self):
11 | # is auto-restart set?
12 | if "restart" in self.args and self.args["restart"] == False:
13 | self.restart = False
14 | else:
15 | self.restart = True
16 |
17 | # is folder watcher set?
18 | if "folder_watcher" in self.args and self.args["folder_watcher"] == True:
19 | self.folder_watcher = True
20 | self.throttle_timer = None
21 | else:
22 | self.folder_watcher = False
23 |
24 | # create a sensor to track check result
25 | self.set_state("sensor.config_result", state="-", attributes = {"friendly_name": "Config Result", "detail": None})
26 |
27 | # get HASS URL
28 | self.apiurl = "{}/api/config/core/check_config".format(self.config["plugins"]["HASS"]["ha_url"])
29 |
30 | # token or key to authenticate
31 | if "token" in self.config["plugins"]["HASS"]:
32 | self.auth = "token"
33 | if self.folder_watcher == False:
34 | self.listen_state(self.check_config, "script.check_config", attribute="last_triggered")
35 | else:
36 | self.listen_event(self.config_throttle, "folder_watcher", file="configuration.yaml")
37 | elif "ha_key" in self.config["plugins"]["HASS"]:
38 | self.auth = "key"
39 | if self.folder_watcher == False:
40 | self.listen_state(self.check_config, "script.check_config", attribute="last_triggered")
41 | else:
42 | self.listen_event(self.config_throttle, "folder_watcher", file="configuration.yaml")
43 | else:
44 | self.log("AppDaemon config must use a token or key to authenticate with HASS")
45 | self.set_state("sensor.config_result", state="ERROR", attributes = {"friendly_name": "Config Result", "detail": "AppDaemon config must use a token or key to authenticate with HASS"})
46 |
47 | def check_config(self, entity, attribute, old, new, kwargs):
48 | # reset sensor while check is in progress
49 | self.set_state("sensor.config_result", state="checking", attributes = {"detail": None})
50 | # set headers for auth
51 | if self.auth == "token":
52 | self.headers = {'Authorization': "Bearer {}".format(self.config["plugins"]["HASS"]["token"])}
53 | else: #key
54 | self.headers = {'x-ha-access': self.config["plugins"]["HASS"]["ha_key"]}
55 | # make the request
56 | r = requests.post(self.apiurl, headers=self.headers)
57 | # evaluate result
58 | if json.loads(r.text)['result'] == "valid":
59 | self.set_state("sensor.config_result", state="valid", attributes = {"detail": None})
60 | # restart if auto-restart is on
61 | if self.restart == True:
62 | self.call_service("homeassistant/restart")
63 | else:
64 | self.set_state("sensor.config_result", state="invalid", attributes = {"detail": json.loads(r.text)['errors']})
65 |
66 | def config_throttle(self, event_name, data, kwargs):
67 | #throttle function to ensure that we don't call check multiple times
68 | self.cancel_timer(self.throttle_timer)
69 | self.throttle_timer = self.run_in(self.auto_check_config, 3)
70 |
71 | def auto_check_config(self, kwargs):
72 | # reset sensor while check is in progress
73 | self.set_state("sensor.config_result", state="checking", attributes = {"detail": None})
74 | # set headers for auth
75 | if self.auth == "token":
76 | self.headers = {'Authorization': "Bearer {}".format(self.config["plugins"]["HASS"]["token"])}
77 | else: #key
78 | self.headers = {'x-ha-access': self.config["plugins"]["HASS"]["ha_key"]}
79 | # make the request
80 | r = requests.post(self.apiurl, headers=self.headers)
81 | # evaluate result
82 | if json.loads(r.text)['result'] == "valid":
83 | self.set_state("sensor.config_result", state="valid", attributes = {"detail": None})
84 | # restart if auto-restart is on
85 | if self.restart == True:
86 | self.call_service("homeassistant/restart")
87 | else:
88 | self.set_state("sensor.config_result", state="invalid", attributes = {"detail": json.loads(r.text)['errors']})
--------------------------------------------------------------------------------
/confignotify.py:
--------------------------------------------------------------------------------
1 | import appdaemon.plugins.hass.hassapi as hass
2 | import globals
3 |
4 | # Notify after CheckConfig runs
5 |
6 | class ConfigNotify(hass.Hass):
7 |
8 | def initialize(self):
9 | # watch for valid config
10 | self.listen_state(self.valid_config, "sensor.config_result")
11 |
12 | # listen for telegram restart callback. only need to initialize this
13 | # listener once
14 | self.listen_event(self.hass_restart, "telegram_callback", data="/restart")
15 |
16 | def valid_config(self, entity, attribute, old, new, kwargs):
17 | if new == "valid":
18 | keyboard = [[("Restart Now", "/restart")]]
19 | self.call_service("telegram_bot/send_message", message = "Config is valid", target = globals.alex_low, inline_keyboard=keyboard)
20 | elif new == "invalid":
21 | error = self.entities.sensor.config_result.attributes.detail
22 | self.call_service("telegram_bot/send_message", message = "Config is invalid", target = globals.alex_low)
23 | self.call_service("telegram_bot/send_message", message = error, target = globals.alex_low)
24 |
25 | def hass_restart(self, event_name, data, kwargs):
26 | self.call_service("telegram_bot/send_message", message = "Home Assistant is restarting", target = globals.alex_low)
27 | self.call_service("homeassistant/restart")
--------------------------------------------------------------------------------
/info.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | **As of version 2.0, I am only testing against AppDaemon 4.x. The app will likely continue to work with
4 | AppDaemon 3.x, but if you have an issue and are using AppDaemon 3.x, I will not be able to provide support.**
5 |
6 | *In addition to configuring this app in `apps.yaml`, you will also need to add a
7 | few lines to `scripts.yaml` and your Lovelace configuration. Or, you will need
8 | to set up the `folder_watcher` component (see the readme for more detail if you
9 | choose that route).*
10 |
11 | ## App Configuration
12 |
13 | Add the following to your `apps.yaml` file:
14 | ```yaml
15 | check_config:
16 | module: checkconfig
17 | class: CheckConfig
18 | ```
19 |
20 | key | optional | type | default | description
21 | -- | -- | -- | -- | --
22 | `module` | False | string | | `checkconfig`
23 | `class` | False | string | | `CheckConfig`
24 | `restart` | True | boolean | True | By default, Home Assistant will be restarted if the configuration is valid. Setting this to false will allow you to restart at the time of your choosing.
25 | `folder_watcher` | True | boolean | False | If you are using the `folder_watcher` component (see "Using with Folder Watcher" in the README), the app can automatically monitor for changes to `configuration.yaml` and run the check any time the file is changed.
26 |
27 | ## `Scripts.yaml` Configuration
28 |
29 | ```yaml
30 | check_config:
31 | sequence: []
32 | alias: Check Configuration
33 | ```
34 |
35 | ## Sample Lovelace Configuration
36 |
37 | ```yaml
38 | type: entities
39 | title: Check Configuration
40 | show_header_toggle: false
41 | entities:
42 | - script.check_config
43 | - sensor.config_result
44 | ```
--------------------------------------------------------------------------------
/lovelace-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apop880/config-check/e653011798d8032c47443705719f81e93e09727a/lovelace-example.png
--------------------------------------------------------------------------------
/picture-elements-example.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apop880/config-check/e653011798d8032c47443705719f81e93e09727a/picture-elements-example.jpeg
--------------------------------------------------------------------------------
/result-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apop880/config-check/e653011798d8032c47443705719f81e93e09727a/result-error.png
--------------------------------------------------------------------------------
/telegram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apop880/config-check/e653011798d8032c47443705719f81e93e09727a/telegram.png
--------------------------------------------------------------------------------