init
This commit is contained in:
1
.HA_VERSION
Normal file
1
.HA_VERSION
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2025.12.5
|
||||||
28
.cloud/acme_account.pem
Normal file
28
.cloud/acme_account.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDL813tfu3dUdiV
|
||||||
|
U9b9vVjWeVIWfhcAyT7JDDI2vWTDTiB9pBMYsT84PwSg35AsELEenMIM1KwU6IyK
|
||||||
|
nI+nLIZkgvAc4QftW1zPBcwJ1O31+LhFG7XTGpCYKIGNpUMzksYvdKkqoV4O6qRs
|
||||||
|
9Viokmt5pvZNQr0ws1a8po00kq6fvUwIMV7Ba725zS7zQCZAADByr6oIqT+czexN
|
||||||
|
vuJuQZMVUdPVyZq0pF8oSCGdpAFIQr8F/xRo17qE52WQ17gxAA69iDfqhET/+wQz
|
||||||
|
JQMp/Vjwp5uX5Agp1tIE2XyskK3MY3YGJWzIg4+A0G9o/njUNvx7pMpw7wCCdA6x
|
||||||
|
84IyXi1lAgMBAAECggEAAW/cxs5Y4zEPL1gooN+LZ3Fx4l4vj84bLuPy259gfR7J
|
||||||
|
DFGSX99p1F3fTLnehGz2roJIvLnn33rW+KgLPBCMfttMUUvFmEbHQ98k1aHmLlA7
|
||||||
|
DiOYl5ztjWDlrseODmg3lMCD65y48q51C5576i4j6zbBsAArIJN25jvfLVJmQBJc
|
||||||
|
Ycie405sZfiK6Ck0sHKxnGYk5/+cqm5/SaoD5h1CNRf1WvfwRO3qRSmD/D1IJ3FS
|
||||||
|
SrF3qwIun9kMeei7+65NycAZAKb61bVdsZMXjThz11uggScdqw10givx/5HwK825
|
||||||
|
Wemu2edBswzpEj+2s9MS3mIi+oweZmQToZXy0+mbaQKBgQD6ZyiBAdEVWih1O/U5
|
||||||
|
L/FWgpW79UsIi98szvPJ8sYCD/ALRijkhZCkIu2r6u8EGZBq0qBZ6b6WnANjpxOS
|
||||||
|
Ms06kzob5uEF9EdpV+zyaE8xUUb9SPoGjdLAD8rIcw2jqaORP/yI6YO2vPcxeKgA
|
||||||
|
UHRpmhKMJUrS6FPPLVnN/gaqaQKBgQDQgmbh8m/+OkDcQQBwyf1ayZEZGiZEWQ3z
|
||||||
|
YcvA87PwL0lq5+TFx9qnb11tYQ9q1OU0c9C8ujnqX3BfE+xnLvdinoSULuRCIRLh
|
||||||
|
KTuZYnUnls04jprTFYCj+YSzG3hfPhzPfe2y5XQ3ZjsHPaiwfTswoqR6Ee2qwV0V
|
||||||
|
J2o2A5bznQKBgQDveMCPwAEJfpO6qoC3FFal+XThsJD1t27UF4em1vru9fcHkS2C
|
||||||
|
fwn5Lz5FcATt0tT+lDiuRJD00HedUiexZcxH/I1SKdeCLkAtSt1cZs11yNkvWh9j
|
||||||
|
LTckXvX8BaxBnPbE7oDBHzHMDaQKN+3Tfx4V8DdUuEV6tp2QQTrlec8+IQKBgBwm
|
||||||
|
ft1ibdxU4QzbecPAgYQQUpahASmZHFkPiwKx5Ek5GSBlzm0lXk/cqTBrOjmiJI/A
|
||||||
|
Ux4nxknuOK2dcv07Sgr2e8/FxOtoq7PabUF4GXkO0wYfuqdk78kzlsbXnpi9OgaJ
|
||||||
|
ad4NPHN+SdngaTXqsmMOkkYoxX2YPYjtmVlRgr/BAoGBALeCe4bHZocSp6YKOCa1
|
||||||
|
h29hJ/ZN7LkI4rzwkOA8JCsSaR0r0p0pJg6uQCf6fTz5UaOhjV+6B1L/O5kqkDxv
|
||||||
|
kKNRtbZ/eeH8FqrZFj6twoZo5Fr7F2TiZIWfyDG1mai7pckHEw1xhhRho9eOGWH/
|
||||||
|
hjRws1Fu0KnaB3YP1g+iT4rM
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
13
.cloud/acme_reg.json
Normal file
13
.cloud/acme_reg.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"body": {
|
||||||
|
"contact": [],
|
||||||
|
"key": {
|
||||||
|
"e": "AQAB",
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "y_Nd7X7t3VHYlVPW_b1Y1nlSFn4XAMk-yQwyNr1kw04gfaQTGLE_OD8EoN-QLBCxHpzCDNSsFOiMipyPpyyGZILwHOEH7VtczwXMCdTt9fi4RRu10xqQmCiBjaVDM5LGL3SpKqFeDuqkbPVYqJJreab2TUK9MLNWvKaNNJKun71MCDFewWu9uc0u80AmQAAwcq-qCKk_nM3sTb7ibkGTFVHT1cmatKRfKEghnaQBSEK_Bf8UaNe6hOdlkNe4MQAOvYg36oRE__sEMyUDKf1Y8Kebl-QIKdbSBNl8rJCtzGN2BiVsyIOPgNBvaP541Db8e6TKcO8AgnQOsfOCMl4tZQ"
|
||||||
|
},
|
||||||
|
"status": "valid"
|
||||||
|
},
|
||||||
|
"terms_of_service": "https://letsencrypt.org/documents/LE-SA-v1.6-August-18-2025.pdf",
|
||||||
|
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/2885294806"
|
||||||
|
}
|
||||||
5
.cloud/production_auth.json
Normal file
5
.cloud/production_auth.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"id_token": "eyJraWQiOiJqZGhOQ1A4U1lRdUZoUTdBS1VuZnM5a0RCXC9YTG0wc2o2MzFKRGhpSWptOD0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIzYWZlMDAxNy1lMWViLTQxOTgtYWJmYi01OGVkZWQ5YTg0ZGIiLCJzdWJfc291cmNlIjoiZGRiX2xlZ2FjeSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJjdXN0b206c3ViLWV4cCI6IjIwMjYtMTItMTciLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV84N2xsNVdPUDgiLCJjb2duaXRvOnVzZXJuYW1lIjoiM2FmZTAwMTctZTFlYi00MTk4LWFiZmItNThlZGVkOWE4NGRiIiwiYXVkIjoiNjBpMnV2aHZiaXJlZjJtZnRqN3JnY3J0OXUiLCJldmVudF9pZCI6IjA2MTcyNDJlLTIyOTgtNDE3ZS1hMDNkLWI4MjVlMzFhMjA2YyIsInRva2VuX3VzZSI6ImlkIiwiYXV0aF90aW1lIjoxNzY1OTQ0MjQxLCJleHAiOjE3Njk4Mzc4NTMsImlhdCI6MTc2OTgzNDI1MywiZW1haWwiOiJjaHJpc3JvYmVydGNoYXN0ZWVuQGdtYWlsLmNvbSJ9.GSIUSy5WfZiIy018M0nns_U4eN7Px8fuunWLGa-wQwV2NLhcVhFodq9_8zhMF5dyqCY9Pw-t5NPyLJbmtByM5eUy8uftpBTXMm0AwUTAzqwnS5mJ7Lbl90NawK4twoqBhNGGXdgsu3LlRpLO0AO9phmMYWOx-qFM3ROmNJCC6VdOY-DcAujN7-MOLtC1p83n55-4KV49RwhdQsLW-t5ANJ6eUkEGR6e_BH2BRiPgEdP_h8uOBBv8IABpc5x17atcmtdrB65mNppafOGPqiyTENmKldwvQwJxDv4VhbV5QQzzkxdE6UETs5ANduF4OauCceKzjlxqMfmCDw_BUw9ZyA",
|
||||||
|
"access_token": "eyJraWQiOiJGTzR4d08xNUJzbDZVVFpwd1ROek8wRVUzeXMxRm1QaERCWmpmU1wvS1llZz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIzYWZlMDAxNy1lMWViLTQxOTgtYWJmYi01OGVkZWQ5YTg0ZGIiLCJldmVudF9pZCI6IjA2MTcyNDJlLTIyOTgtNDE3ZS1hMDNkLWI4MjVlMzFhMjA2YyIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE3NjU5NDQyNDEsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtZWFzdC0xXzg3bGw1V09QOCIsImV4cCI6MTc2OTgzNzg1MywiaWF0IjoxNzY5ODM0MjUzLCJqdGkiOiI5YzJmNmI1Yy1mZWE1LTRkYmEtOGExMy01ZjJhY2QzZTE5ZTIiLCJjbGllbnRfaWQiOiI2MGkydXZodmJpcmVmMm1mdGo3cmdjcnQ5dSIsInVzZXJuYW1lIjoiM2FmZTAwMTctZTFlYi00MTk4LWFiZmItNThlZGVkOWE4NGRiIn0.RSBe4mQoVVCB6r0rlB03xls2M4U2y70pbCXPYHTqRoSqdLwy7nVqD1rN4Frq4zgc7Sw3w9lgXOAm7kPc-1KDIE-bcGw-VVY7VhsjjxBuqfNdp_ZcNpYLl1QziJAkJMaoQ1hxSqF-lzr_0r3v19NvBTT8iHNMHh0taOnAgjlkD7_qQAE0bb_0hVs_eiZYL8cUt_j37NAklt7NePLEkODlywPM2sOYN0HB8gFR6BemImeK3njwKh91WNY0u2alJFftch9-PdHs5ezAb_1QgUnUVYyQfLMfYo-obp-r0CKCajyhDJ-Lsl3sK0nPK7LIkaZgU_tZuQo18OIAF9etv4Or2A",
|
||||||
|
"refresh_token": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.NcXogyi12pNEpyYbSr-5fr5BEyJgIKIsA9jIcSOb7RQvAjQSvu35HXp8g6RF107MhLfS6rjNpD_TXEpBQubPM-BtFwYJztfX39UEKRtndETQwL4tEB8IissG80yld8lACFk5WO-Uw4r0BfdYEBSXnjhbZVzROXr9x_xzA7TjC_IeYLbt70mnisqEb-4KK_EO91aYNMhDd7JRnqAHdUdXDyLBciYTY76j8hRYniOwm4NRcl8WZzbYNCpTjyyrwJnDuZdALcWvqU9I_oN7UiWTVl0w9ESE6WN2FzylMrVsTUPhpsTHcM1LzLoipHhQljeIAUTnpImWoGSnjz-U_VDEeg.oLWMDRxUYKixRPtP.H4VnlFIn765-xvlVqOs4-84fPq0j0irjGOz2Ke9qPKkmJKAX3v_3JZwiEYwzjZgsguEYXBfIuF3ATq2ZN-os8cFNk4pXQcy7UpXYHAzQgayu6r0-9rzoGIYxHftB7kQbOM31p_AbnaxwBa_PLyluQEdq45lWbdIcN3SlUmkmf5O8gmw-cjgbAswHyP-UFJWgh7OQ0doIqJW7HWB8g0e44EG5iUl_tRY44Pja0k3tuvifkyBoAG2QEO27c-uNdXaLk4iYBamBhY8HPx69QPVNSH8ImrqXWVXHcSf7iUEj_WhuH_05qakSli-CPGir3Yja8ozjhn-cW2oRdZcV7fc8ZG6c54WaI22Mek21Br4Cn9yG2cTRQHe0XlD_JBASdfvTyFtotvevjkpF4c3hjAABX94-U3cCW37bR6zY7g8IKKy7zoZX3RtgNRHIlMZ8T9g3VGlDZ6yvoEyKq67MS3_hr-skHjIZpxspEAgQtU5tJhwn2rxI7wYRvFpYQusqMYPa1fEZUb4eV7puEmKdKUlVQ_YX4F-RAL3MNNKMHJBHMtJ6TPBe9Ok_wl4KKzZZjS2ZsETz3ZcnbqMH1zYVCpIW4Dsr5Qc2L7dqgKsjDm9-r9erAxNJMFQvcA4WTzgyRgEqyNgAOpdwTZd7xXGbBSLF7rJ-FgQu1C4DOdE-ZvTJlsg2EcA8BXC3R4I0saXFDTYse_DePqn1YsDXotdeATi6nTeWgPOMl9Ashk35lTBnKgkwX5yc8_-iPBUfTwVaOA7Yn1B1MHnN6_scuT_EmUM4kB7_aUuawbo_NuhPJ_dSaMHrqrc-9TCGpSttcmX73CbrZKlrcIRvX3CyLuMbzvuYHli3003l18RhEBheaDr9KUGN3nMeooxgQoOFFl03Ibw8CoI0joGAqgGU0YCVx8ISZV7t7Cx2DTg2Ht6YNs3xZkmtDx_c8bj-QJfRxf9HDKKGHioOtRmovv1YlWuW8iHcYS2lh59GZ_etQolWfwS_Vq6R6cQctuiebIc80NAbnZNYXRHPHDKzX3fscR7eDmAywP8WB65YeAYmzGKeGt7ST7qgAVoWlZad97IMb41pLGCbd-dd49NF_XxJxbqOu-QRDoQ0-hwt2lrwCT_Zm0v2SWf5hl0aNXWT6pw5J_cOPyATKPC81ANFIONs_cXDevOsKzRFDwg8L4pgKn_mufsdF_w98il5Yx3EGfQxKOlDZsFedMY93KqpuTmzGHot9fd1_JsowA1iTRhEYM6Q1CipgcEqKoAkpbrkm1j8whc0orKXVkXR7il2gvqL8T4nWHyso9QuPlrD9aiU1ue6S_2GmIhfC6zGqbWHBxJpQ_Ev3Q.JDKlK0S5cpYzFNmFcKVAHw"
|
||||||
|
}
|
||||||
61
.cloud/remote_fullchain.pem
Normal file
61
.cloud/remote_fullchain.pem
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFSjCCBDKgAwIBAgISBRdgzVwLLHjVBRf2MqWC/8EHMA0GCSqGSIb3DQEBCwUA
|
||||||
|
MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD
|
||||||
|
EwNSMTIwHhcNMjUxMjE3MDQxNzAzWhcNMjYwMzE3MDQxNzAyWjA4MTYwNAYDVQQD
|
||||||
|
Ey1kbHdnc3Iya3dvb3ZlajQxaXR2amtjN2NhYnR5bWZwMy51aS5uYWJ1LmNhc2Ew
|
||||||
|
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCLQGcrdY1EBhh3VuhEp4sM
|
||||||
|
BxpmRAHjJES6+eDFjJP+cGdeYQbUKmqNPcBmG4dTAVlRWlANHA4/wKp5cWkD2zgY
|
||||||
|
EA5aUSiDS9uYxDx/L/+x1A9r/i+36benoga+Mdp6mY9d2EwSoaUakl/4ezXHmC3O
|
||||||
|
H83W41QL7mlsTcqJiQ93nCDVaxM/wwLugjKpQFX41qrmptr413Ep2fro53p2TYvW
|
||||||
|
E8XXBsHazb3fPbEDPNO6aTDlsjyizQz+BkGeUUbuHeESZLYky3sjYEZKmCHZusTI
|
||||||
|
LP3bCd/GQP6Mx04davAXfJCw1FsCZnJLvp5/30m43uIQiOpRWHusPxp3bwBlzoP7
|
||||||
|
AgMBAAGjggJRMIICTTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH
|
||||||
|
AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPcU+gXZtVD57D8r
|
||||||
|
/VR5OE844iIuMB8GA1UdIwQYMBaAFAC1KfItjm8x6JtMrXg++tzpDNHSMDMGCCsG
|
||||||
|
AQUFBwEBBCcwJTAjBggrBgEFBQcwAoYXaHR0cDovL3IxMi5pLmxlbmNyLm9yZy8w
|
||||||
|
TgYDVR0RBEcwRYItZGx3Z3NyMmt3b292ZWo0MWl0dmprYzdjYWJ0eW1mcDMudWku
|
||||||
|
bmFidS5jYXNhghRob21lLmJvb2tzYW5kYm9wcy51czATBgNVHSAEDDAKMAgGBmeB
|
||||||
|
DAECATAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8vcjEyLmMubGVuY3Iub3JnLzgu
|
||||||
|
Y3JsMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHUAFoMtq/CpJQ8P8DqlRf/Iv8gj
|
||||||
|
0IdL9gQpJ/jnHzMT9foAAAGbKrwpYwAABAMARjBEAiB8Txgs6WCzDQN/6paWk+01
|
||||||
|
qWZiEi/QN3qxqDsMGTFrsgIgBKBVV9lmrx3As4ubDI/ORj5iIhJ9E+knZzEN8W+4
|
||||||
|
hGQAdgAOV5S8866pPjMbLJkHs/eQ35vCPXEyJd0hqSWsYcVOIQAAAZsqvDEHAAAE
|
||||||
|
AwBHMEUCIC4SfSql7nTK5BWNulG6oh+vs5Lyb3nTidIuOCUFrdiCAiEAo5CQzleQ
|
||||||
|
VKWH3bCtnn+ScWpC2wP8YC+ohVmPTtvMWuUwDQYJKoZIhvcNAQELBQADggEBALxf
|
||||||
|
1UZwk0GLz0IeYFLUg5B1O1i86iDgCuA8GnKmJG9hnU7kKcv7nDiFuC7A2csACmVy
|
||||||
|
Vn+SkCDhlP2UOQq7O6zaSPjd7KGrEdfl4WAS2kHoOR8RkJhg5t4HuJQR4WekoWcI
|
||||||
|
n+nDa4EEHzhWzVX66D/lY0DaISXSX8K2cxW2T1zJVepI62lE9z246vf+NE2JqpHq
|
||||||
|
QC8wWKZI1aPQ06MQBDer6W7CDiATCwRaG8yKWfkH73puvPcJiRzTlB4CWHggFhBT
|
||||||
|
/7Hxzidf25B25S9nyFglZlSNgzEw4RMuTe3qJKaTmbv0WPzEKI7xV7k09IAi7kh7
|
||||||
|
CP598yDPn765ztqKGMs=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFBjCCAu6gAwIBAgIRAMISMktwqbSRcdxA9+KFJjwwDQYJKoZIhvcNAQELBQAw
|
||||||
|
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||||
|
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw
|
||||||
|
WhcNMjcwMzEyMjM1OTU5WjAzMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
|
||||||
|
RW5jcnlwdDEMMAoGA1UEAxMDUjEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||||
|
CgKCAQEA2pgodK2+lP474B7i5Ut1qywSf+2nAzJ+Npfs6DGPpRONC5kuHs0BUT1M
|
||||||
|
5ShuCVUxqqUiXXL0LQfCTUA83wEjuXg39RplMjTmhnGdBO+ECFu9AhqZ66YBAJpz
|
||||||
|
kG2Pogeg0JfT2kVhgTU9FPnEwF9q3AuWGrCf4yrqvSrWmMebcas7dA8827JgvlpL
|
||||||
|
Thjp2ypzXIlhZZ7+7Tymy05v5J75AEaz/xlNKmOzjmbGGIVwx1Blbzt05UiDDwhY
|
||||||
|
XS0jnV6j/ujbAKHS9OMZTfLuevYnnuXNnC2i8n+cF63vEzc50bTILEHWhsDp7CH4
|
||||||
|
WRt/uTp8n1wBnWIEwii9Cq08yhDsGwIDAQABo4H4MIH1MA4GA1UdDwEB/wQEAwIB
|
||||||
|
hjAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB
|
||||||
|
/wIBADAdBgNVHQ4EFgQUALUp8i2ObzHom0yteD763OkM0dIwHwYDVR0jBBgwFoAU
|
||||||
|
ebRZ5nu25eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAC
|
||||||
|
hhZodHRwOi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcG
|
||||||
|
A1UdHwQgMB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcN
|
||||||
|
AQELBQADggIBAI910AnPanZIZTKS3rVEyIV29BWEjAK/duuz8eL5boSoVpHhkkv3
|
||||||
|
4eoAeEiPdZLj5EZ7G2ArIK+gzhTlRQ1q4FKGpPPaFBSpqV/xbUb5UlAXQOnkHn3m
|
||||||
|
FVj+qYv87/WeY+Bm4sN3Ox8BhyaU7UAQ3LeZ7N1X01xxQe4wIAAE3JVLUCiHmZL+
|
||||||
|
qoCUtgYIFPgcg350QMUIWgxPXNGEncT921ne7nluI02V8pLUmClqXOsCwULw+PVO
|
||||||
|
ZCB7qOMxxMBoCUeL2Ll4oMpOSr5pJCpLN3tRA2s6P1KLs9TSrVhOk+7LX28NMUlI
|
||||||
|
usQ/nxLJID0RhAeFtPjyOCOscQBA53+NRjSCak7P4A5jX7ppmkcJECL+S0i3kXVU
|
||||||
|
y5Me5BbrU8973jZNv/ax6+ZK6TM8jWmimL6of6OrX7ZU6E2WqazzsFrLG3o2kySb
|
||||||
|
zlhSgJ81Cl4tv3SbYiYXnJExKQvzf83DYotox3f0fwv7xln1A2ZLplCb0O+l/AK0
|
||||||
|
YE0DS2FPxSAHi0iwMfW2nNHJrXcY3LLHD77gRgje4Eveubi2xxa+Nmk/hmhLdIET
|
||||||
|
iVDFanoCrMVIpQ59XWHkzdFmoHXHBV7oibVjGSO7ULSQ7MJ1Nz51phuDJSgAIU7A
|
||||||
|
0zrLnOrAj/dfrlEWRhCvAgbuwLZX1A2sjNjXoPOHbsPiy+lO1KF8/XY7
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
.cloud/remote_private.pem
Normal file
28
.cloud/remote_private.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLQGcrdY1EBhh3
|
||||||
|
VuhEp4sMBxpmRAHjJES6+eDFjJP+cGdeYQbUKmqNPcBmG4dTAVlRWlANHA4/wKp5
|
||||||
|
cWkD2zgYEA5aUSiDS9uYxDx/L/+x1A9r/i+36benoga+Mdp6mY9d2EwSoaUakl/4
|
||||||
|
ezXHmC3OH83W41QL7mlsTcqJiQ93nCDVaxM/wwLugjKpQFX41qrmptr413Ep2fro
|
||||||
|
53p2TYvWE8XXBsHazb3fPbEDPNO6aTDlsjyizQz+BkGeUUbuHeESZLYky3sjYEZK
|
||||||
|
mCHZusTILP3bCd/GQP6Mx04davAXfJCw1FsCZnJLvp5/30m43uIQiOpRWHusPxp3
|
||||||
|
bwBlzoP7AgMBAAECggEABc0S2NDWp/A1Eeuqu2/bYnUW5XTzbmBmOJlzHE4evQMD
|
||||||
|
wcvHxz9V6jjM8uc2SP/z/Gg6kE4MTepoPHm8BtWzTYiFs7+Dyr+pqbPEujiB4Qk7
|
||||||
|
jaOTBO9iOXdauCh6UipjIMuDYWQe7UkOlHRvKVFIPzkJKZv8AvWiQ+L6iz/ZeoF7
|
||||||
|
CvDfgEb9bO7U/Tk7domjiyhPjqZOObmfTyJq1WDPOgbwdACkZBVFBlds4zdDrbCd
|
||||||
|
pX5ggW+dNWHWzmWpbAcNo9C4uT/4OST4HcyK0gkxXdqgyJeq8uhCirmdom8OMvGc
|
||||||
|
gFLtVIUFT388yh0b9RLnXFBC/2LJ99kfGYqIw1i3mQKBgQDA6eyppDUc+xOKgmqM
|
||||||
|
2uOreiQXFbnZNsc9AZlz30adwvIeFjJz3dQ1i4qDeVMjVLNAPPd1PdRlsbfZ6Ir9
|
||||||
|
H08A4P5vvOlwHPFOdSKE2mGcZwQcurqqFm9o1TB2K+RNzS5UiBPdmyvoMqG0d9Jr
|
||||||
|
VE8SdIw2UHQL4AgmeAkvG0U8PQKBgQC4yhCSMbiu9HofUrcw5mlSpqQA/SOgArCt
|
||||||
|
l6Ea/m2fQGenSjTT99SnUzBaUUhdeZSL7OUy3il7kOHc1tSAXwz65B8EwOVMnPdV
|
||||||
|
8L1mEkTd1MYQs3+eA2HUcrGlSVHqZXpgNXuRFvg031uisubGOLYG2sodhfdhurWi
|
||||||
|
yld69muslwKBgQC28mCc9HvmMvlyJoHzeHXmndtyBQmNxBQod8bWC01FuaWxAK3y
|
||||||
|
EdH4wY+nZTyBygaAChFHH0647lQDlDOTHsjmdXj18HqU9u2k0RLeWNeu4kcVE8SI
|
||||||
|
HuSiz2K4/qDxY37nbXEhfNGjz7holCV54adnQh3iOGQFCv3PtZBIGx2KpQKBgGY9
|
||||||
|
/x80fY/n2u4b0RowUlQVuaaGaUCuXF0gCVarMbIsa22HRGWHuVR/VcCTOqvlikhF
|
||||||
|
Yadcfq1Mw3tyLg99B+yFbZgutnBGZR9a3SBtuUbX5GL3PgQKsQVgFGR0hetgDG7R
|
||||||
|
CLaFc/2lG8mQnNlOJYDza2McbXzYVolk1TRGxdqvAoGBALegpkxgZDwGrWkt5Upm
|
||||||
|
dbIse9pHSCoS0K6dkbMXQ5juFRJ9JC8hVDTjV+Sl1kJX2Yxtzyvij1eoTSVdZhbk
|
||||||
|
GSHHG0EsN9MdWx+1hdd4YVZCfRLZ2nx+E1wBpwA6sXzycLbF2J9keNJy8MAj81XU
|
||||||
|
+tJtXq5VMEsVQei9jyADSYO5
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
1
.ha_run.lock
Normal file
1
.ha_run.lock
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"pid": 67, "version": 1, "ha_version": "2025.12.5", "start_ts": 1767163487.6344073}
|
||||||
7
.shopping_list.json
Normal file
7
.shopping_list.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Cancel AA card by July if there is still a fee",
|
||||||
|
"id": "2ab4a3762cb8457788c153501f797884",
|
||||||
|
"complete": false
|
||||||
|
}
|
||||||
|
]
|
||||||
91
.storage/alarmo.storage
Normal file
91
.storage/alarmo.storage
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"version": 6,
|
||||||
|
"minor_version": 3,
|
||||||
|
"key": "alarmo.storage",
|
||||||
|
"data": {
|
||||||
|
"config": {
|
||||||
|
"code_arm_required": false,
|
||||||
|
"code_mode_change_required": false,
|
||||||
|
"code_disarm_required": true,
|
||||||
|
"code_format": "number",
|
||||||
|
"disarm_after_trigger": false,
|
||||||
|
"ignore_blocking_sensors_after_trigger": false,
|
||||||
|
"master": {
|
||||||
|
"enabled": true,
|
||||||
|
"name": "master"
|
||||||
|
},
|
||||||
|
"mqtt": {
|
||||||
|
"enabled": false,
|
||||||
|
"state_topic": "alarmo/state",
|
||||||
|
"state_payload": {},
|
||||||
|
"command_topic": "alarmo/command",
|
||||||
|
"command_payload": {},
|
||||||
|
"require_code": true,
|
||||||
|
"event_topic": "alarmo/event"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"area_id": "1765607864",
|
||||||
|
"name": "Alarmo",
|
||||||
|
"modes": {
|
||||||
|
"armed_away": {
|
||||||
|
"enabled": true,
|
||||||
|
"exit_time": 60,
|
||||||
|
"entry_time": 60,
|
||||||
|
"trigger_time": 1800
|
||||||
|
},
|
||||||
|
"armed_home": {
|
||||||
|
"enabled": true,
|
||||||
|
"exit_time": null,
|
||||||
|
"entry_time": null,
|
||||||
|
"trigger_time": 1800
|
||||||
|
},
|
||||||
|
"armed_vacation": {
|
||||||
|
"enabled": true,
|
||||||
|
"exit_time": 60,
|
||||||
|
"entry_time": 60,
|
||||||
|
"trigger_time": 1800
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sensors": [
|
||||||
|
{
|
||||||
|
"entity_id": "binary_sensor.night_light_1_motion",
|
||||||
|
"type": "motion",
|
||||||
|
"modes": [
|
||||||
|
"armed_away",
|
||||||
|
"armed_vacation"
|
||||||
|
],
|
||||||
|
"use_exit_delay": true,
|
||||||
|
"use_entry_delay": true,
|
||||||
|
"always_on": false,
|
||||||
|
"arm_on_close": false,
|
||||||
|
"allow_open": true,
|
||||||
|
"trigger_unavailable": false,
|
||||||
|
"auto_bypass": false,
|
||||||
|
"auto_bypass_modes": [],
|
||||||
|
"area": "1765607864",
|
||||||
|
"enabled": true,
|
||||||
|
"entry_delay": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"user_id": "1765608036",
|
||||||
|
"name": "Chris",
|
||||||
|
"enabled": true,
|
||||||
|
"code": "JDJiJDEwJFdIaUxManNTZTR0bmphcEd1ZkVpcHVIamlWSk1lai5rV2lvRjRFMnIvWGRWZG1QdUxTaFZ5",
|
||||||
|
"can_arm": true,
|
||||||
|
"can_disarm": true,
|
||||||
|
"is_override_code": false,
|
||||||
|
"code_format": "number",
|
||||||
|
"code_length": 4,
|
||||||
|
"area_limit": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"automations": [],
|
||||||
|
"sensor_groups": []
|
||||||
|
}
|
||||||
|
}
|
||||||
8
.storage/alexa
Normal file
8
.storage/alexa
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "alexa",
|
||||||
|
"data": {
|
||||||
|
"authorized": true
|
||||||
|
}
|
||||||
|
}
|
||||||
16
.storage/application_credentials
Normal file
16
.storage/application_credentials
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "application_credentials",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "google_555321766228_fr9p85c52459cgp2d5qbnpkvnh9rha58_apps_googleusercontent_com",
|
||||||
|
"domain": "google",
|
||||||
|
"client_id": "555321766228-fr9p85c52459cgp2d5qbnpkvnh9rha58.apps.googleusercontent.com",
|
||||||
|
"client_secret": "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",
|
||||||
|
"name": "Home Todo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
55
.storage/assist_pipeline.pipelines
Normal file
55
.storage/assist_pipeline.pipelines
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 2,
|
||||||
|
"key": "assist_pipeline.pipelines",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"conversation_engine": "conversation.home_assistant",
|
||||||
|
"conversation_language": "en",
|
||||||
|
"id": "01je2pa13016yx8r5pepwdhnza",
|
||||||
|
"language": "en",
|
||||||
|
"name": "Home Assistant",
|
||||||
|
"stt_engine": "stt.home_assistant_cloud",
|
||||||
|
"stt_language": "en-US",
|
||||||
|
"tts_engine": "tts.home_assistant_cloud",
|
||||||
|
"tts_language": "en-US",
|
||||||
|
"tts_voice": "AriaNeural",
|
||||||
|
"wake_word_entity": null,
|
||||||
|
"wake_word_id": null,
|
||||||
|
"prefer_local_intents": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"conversation_engine": "conversation.home_assistant",
|
||||||
|
"conversation_language": "en",
|
||||||
|
"id": "01jfp2jjrck1222zqhdv41exwv",
|
||||||
|
"language": "en",
|
||||||
|
"name": "Home Assistant Cloud",
|
||||||
|
"stt_engine": "stt.home_assistant_cloud",
|
||||||
|
"stt_language": "en-US",
|
||||||
|
"tts_engine": "tts.home_assistant_cloud",
|
||||||
|
"tts_language": "en-US",
|
||||||
|
"tts_voice": "JennyNeural",
|
||||||
|
"wake_word_entity": null,
|
||||||
|
"wake_word_id": null,
|
||||||
|
"prefer_local_intents": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"conversation_engine": "conversation.home_assistant",
|
||||||
|
"conversation_language": "en",
|
||||||
|
"id": "01k3wj61hbbjyfx892xzxx1wej",
|
||||||
|
"language": "en",
|
||||||
|
"name": "Local Assistant",
|
||||||
|
"stt_engine": "stt.speech_to_phrase",
|
||||||
|
"stt_language": "en",
|
||||||
|
"tts_engine": "tts.piper",
|
||||||
|
"tts_language": "en_US",
|
||||||
|
"tts_voice": "en_US-lessac-high",
|
||||||
|
"wake_word_entity": null,
|
||||||
|
"wake_word_id": null,
|
||||||
|
"prefer_local_intents": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preferred_item": "01je2pa13016yx8r5pepwdhnza"
|
||||||
|
}
|
||||||
|
}
|
||||||
287
.storage/auth
Normal file
287
.storage/auth
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "auth",
|
||||||
|
"data": {
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": "23e04b69df624a0fbff05ad27b5a1f0a",
|
||||||
|
"group_ids": [
|
||||||
|
"system-read-only"
|
||||||
|
],
|
||||||
|
"is_owner": false,
|
||||||
|
"is_active": true,
|
||||||
|
"name": "Home Assistant Content",
|
||||||
|
"system_generated": true,
|
||||||
|
"local_only": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5e62410fa88e4aa7ab4ac7b9c4ba5dc1",
|
||||||
|
"group_ids": [
|
||||||
|
"system-admin"
|
||||||
|
],
|
||||||
|
"is_owner": false,
|
||||||
|
"is_active": true,
|
||||||
|
"name": "Supervisor",
|
||||||
|
"system_generated": true,
|
||||||
|
"local_only": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"group_ids": [
|
||||||
|
"system-admin"
|
||||||
|
],
|
||||||
|
"is_owner": true,
|
||||||
|
"is_active": true,
|
||||||
|
"name": "Christopher Chasteen",
|
||||||
|
"system_generated": false,
|
||||||
|
"local_only": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9ba78f5ab4e646758f3c093bd79ac9c8",
|
||||||
|
"group_ids": [
|
||||||
|
"system-admin"
|
||||||
|
],
|
||||||
|
"is_owner": false,
|
||||||
|
"is_active": true,
|
||||||
|
"name": "Gaby",
|
||||||
|
"system_generated": false,
|
||||||
|
"local_only": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5f60cc00c4f249e3894b3148caccc231",
|
||||||
|
"group_ids": [
|
||||||
|
"system-admin"
|
||||||
|
],
|
||||||
|
"is_owner": false,
|
||||||
|
"is_active": true,
|
||||||
|
"name": "Home Assistant Cloud",
|
||||||
|
"system_generated": true,
|
||||||
|
"local_only": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "system-admin",
|
||||||
|
"name": "Administrators"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "system-users",
|
||||||
|
"name": "Users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "system-read-only",
|
||||||
|
"name": "Read Only"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credentials": [
|
||||||
|
{
|
||||||
|
"id": "52deb678d729425b9b028dddde7ac87a",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"auth_provider_type": "homeassistant",
|
||||||
|
"auth_provider_id": null,
|
||||||
|
"data": {
|
||||||
|
"username": "cchasteen99"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5d346781e9be4d4c8324f6b0df61f273",
|
||||||
|
"user_id": "9ba78f5ab4e646758f3c093bd79ac9c8",
|
||||||
|
"auth_provider_type": "homeassistant",
|
||||||
|
"auth_provider_id": null,
|
||||||
|
"data": {
|
||||||
|
"username": "gaby"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh_tokens": [
|
||||||
|
{
|
||||||
|
"id": "6d75dc71b2114f498af0cf7dff68c828",
|
||||||
|
"user_id": "23e04b69df624a0fbff05ad27b5a1f0a",
|
||||||
|
"client_id": null,
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "system",
|
||||||
|
"created_at": "2024-12-02T03:23:29.428978+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "0497acf0545d56e8105d6e404fce1bdad392981b4392ef49aa7b094cc0d1dda54dd5a018ec7825c79af3d726ab7d87d9072c9afa5e8282c5a47feb13f6283a19",
|
||||||
|
"jwt_key": "ac27bccb4303fa53bf33e7d3284e244ceea94ba596049eb67279da5db44783dda6803b6c0e54941761f89121ea9df300d867228f47c0761f43432f16dd7cf379",
|
||||||
|
"last_used_at": null,
|
||||||
|
"last_used_ip": null,
|
||||||
|
"expire_at": null,
|
||||||
|
"credential_id": null,
|
||||||
|
"version": "2024.11.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fbb4f0f123ab4998861d270407b33e63",
|
||||||
|
"user_id": "5e62410fa88e4aa7ab4ac7b9c4ba5dc1",
|
||||||
|
"client_id": null,
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "system",
|
||||||
|
"created_at": "2024-12-02T03:23:29.575481+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "d3ac2bf124ba5c79f5e17369eb8ee4eff291f0a9be35fe217fb8d64f6ea63b6add2655f78de2508d66f182e7fa0cfb74d7dff01a35b588ae83408d49ace1b6e6",
|
||||||
|
"jwt_key": "459e08285d3d747c62642c5ae49ac21bc868c8e158c3a126b6fbfb5f5b14b1d9d4023851bebcabba69dc0d18ce003ccc60a4bb7a13b8e8b99145ef28e684c40f",
|
||||||
|
"last_used_at": "2026-01-31T05:20:55.136543+00:00",
|
||||||
|
"last_used_ip": "172.30.32.2",
|
||||||
|
"expire_at": null,
|
||||||
|
"credential_id": null,
|
||||||
|
"version": "2024.11.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "704364b1f4e4428c995916b69cedfeef",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": "https://home-assistant.io/android",
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "normal",
|
||||||
|
"created_at": "2024-12-22T00:24:53.536492+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "837c0a76c5b2d1a95e39b397a50b1bb0c8057285e82775a42b3581c4305c677a285afe2700f6cea49f72f06bfb7b75ca0044aba658acf9bd6210b602dbe65ae4",
|
||||||
|
"jwt_key": "64176f4819042bbbe8dd895c73d187b3bcffa75e54ac4e5bbd7219061daac65dc78b33ac85b5e839fbcf02fd272e2dc3bc0b959000099489c3a44591baa73c15",
|
||||||
|
"last_used_at": "2025-12-17T05:08:12.117027+00:00",
|
||||||
|
"last_used_ip": "127.0.0.1",
|
||||||
|
"expire_at": 1773724092.117027,
|
||||||
|
"credential_id": "52deb678d729425b9b028dddde7ac87a",
|
||||||
|
"version": "2024.12.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4b3cc63775a442678e4960cd65d3a47c",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": "http://homeassistant.local:8123/",
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "normal",
|
||||||
|
"created_at": "2025-05-20T03:23:23.159821+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "ec1a87659e469a457b43d93f4c03d6e3f6ea4c56c6930bef2fc22cb82479bbb3c2bd10478acea51b66a0e9086869cdd0c8a9f7fd8d6c8d5fd0b09bf615951123",
|
||||||
|
"jwt_key": "4a66d37ba9ce8e808f1ad432f810389dab7949ab35cb6faed314b535332d61b93b8855ab149abf374c8ca2480ce3f98e232fb84d59e8fc6e3e71eb461a2b0915",
|
||||||
|
"last_used_at": "2025-12-17T02:50:00.545690+00:00",
|
||||||
|
"last_used_ip": "192.168.0.230",
|
||||||
|
"expire_at": 1773715800.54569,
|
||||||
|
"credential_id": "52deb678d729425b9b028dddde7ac87a",
|
||||||
|
"version": "2025.2.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ef12c765610f4858be358423fab344b3",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": "http://homeassistant.local:8123/",
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "normal",
|
||||||
|
"created_at": "2025-09-20T23:31:23.642363+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "79e79506bd209926860e0c3bb10f8d6d89742bf1241dae4ba8626282dca088468ed13d0a8356fbb4ebc47adad404b03d7a2c0153f25fba2d8e4725e8e4c23d87",
|
||||||
|
"jwt_key": "89d88eae84aa1047589c8a5a1aaf60ec7b2bb1b361ce7ce974d1573445f7480dbe7ad4af028ad1c06c679a8316981c21a9d34c7a141749d421fcbef87c50cfae",
|
||||||
|
"last_used_at": "2025-12-17T02:14:45.852533+00:00",
|
||||||
|
"last_used_ip": "192.168.0.123",
|
||||||
|
"expire_at": 1773713685.852533,
|
||||||
|
"credential_id": "52deb678d729425b9b028dddde7ac87a",
|
||||||
|
"version": "2025.9.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5749ca3473be40f6beac7a5eb1f47203",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": null,
|
||||||
|
"client_name": "Bruno",
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "long_lived_access_token",
|
||||||
|
"created_at": "2025-11-23T18:53:14.867761+00:00",
|
||||||
|
"access_token_expiration": 315360000.0,
|
||||||
|
"token": "8df3a254dd2afd475424ca6da2ff92a757e9332b63f06d55c74e31f5f724654c2afdfaffdf929f23517038cfed23a8481ec3097d21717af8cf0bb4cc6ef47eb1",
|
||||||
|
"jwt_key": "8d62520fbd3d3ad9fe787dd929e8748f0df23845cffefe5e7c21151573aee8cb0b44c06a72c77d275b0806d1fd3af09a43e20a63dd2cb60db144aedd5a438651",
|
||||||
|
"last_used_at": "2025-11-23T18:53:14.867903+00:00",
|
||||||
|
"last_used_ip": null,
|
||||||
|
"expire_at": null,
|
||||||
|
"credential_id": null,
|
||||||
|
"version": "2025.11.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8a068792df4d49cca291252014104743",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": "http://192.168.0.107:8123/",
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "normal",
|
||||||
|
"created_at": "2025-12-17T03:08:25.443606+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "58718e7aae911fb523229e47f75fe0b75e1dff07f814863a2ac2715f27cce78aec7cadc48d627ab05e669269434ac1573aba0de7844f386b9ef48705a8a5cf4e",
|
||||||
|
"jwt_key": "79b3db5567d7f3cad9c9231c705cccbe0125e32fc8bf025ca501a40200ead8e35498b5a91d69c30af43f2ddd359148d39df1b6183b73afd167a7b171d4609e4c",
|
||||||
|
"last_used_at": "2026-01-31T04:35:48.406826+00:00",
|
||||||
|
"last_used_ip": "192.168.0.232",
|
||||||
|
"expire_at": 1777610148.406826,
|
||||||
|
"credential_id": "52deb678d729425b9b028dddde7ac87a",
|
||||||
|
"version": "2025.12.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "24706e287c5841358cc7beb68c9067fa",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": "https://home.booksandbops.us/",
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "normal",
|
||||||
|
"created_at": "2025-12-17T04:36:12.132968+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "3cf55728f26dda23ec1867cc1342e70a2f651db4d46ac532b11e1927f05ed3639c5ece5488a03beefcb902a453e5270ff7233121cd29721f63e10636544bcaa3",
|
||||||
|
"jwt_key": "4e595a8f7a8cfff58f7826eeb52aa113a89a9d12f2881e11a1641032970e0f6e6c33ddb4f08a4350660777f2deeb93cb15172ca669647637287e2240d6135409",
|
||||||
|
"last_used_at": "2025-12-17T05:12:41.246519+00:00",
|
||||||
|
"last_used_ip": "127.0.0.1",
|
||||||
|
"expire_at": 1773724361.246519,
|
||||||
|
"credential_id": "52deb678d729425b9b028dddde7ac87a",
|
||||||
|
"version": "2025.12.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ccceefe94cac4657a15d0fa5994a916b",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": "https://home-assistant.io/android",
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "normal",
|
||||||
|
"created_at": "2025-12-17T05:23:10.854950+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "5c55e3e9992dcbf2fc27f574e587577b0f5927b01f34c89d125f8476c4b55f31c4a02ec0405113f628a4abebb52532829e3bab0123208d83128f299c12c34d4c",
|
||||||
|
"jwt_key": "2ff0f6cfdcf1d99c514c29a814d47bc9515912493475e351c5682fd47f8fbf1924e35c5fb9174fef1b9254bab385a5d4b7238bc834d0c58c1543383dba62d0af",
|
||||||
|
"last_used_at": "2026-01-30T04:32:28.151064+00:00",
|
||||||
|
"last_used_ip": "192.168.0.194",
|
||||||
|
"expire_at": 1777523548.151064,
|
||||||
|
"credential_id": "52deb678d729425b9b028dddde7ac87a",
|
||||||
|
"version": "2025.12.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f9e9618e8af944758381e2da407fff7b",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"client_id": null,
|
||||||
|
"client_name": "python",
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "long_lived_access_token",
|
||||||
|
"created_at": "2025-12-31T06:02:15.664635+00:00",
|
||||||
|
"access_token_expiration": 315360000.0,
|
||||||
|
"token": "2612d35c2bc27aef742a7bfd2e9deda1d46e8e77832d3a95c4b44e7b98e89883b5b47600a6890c2d9da6192354c07b3e1e71de89db1995a620237436c8f1797f",
|
||||||
|
"jwt_key": "657d49e9c6ef506c4785fe1e84be9a54a7b4333394f49545160d1f9d948a1a9452c9e1bd9a12d753fbf3bcf1fe0f9082b05df5bbe137e7735121c424704853d0",
|
||||||
|
"last_used_at": "2025-12-31T06:02:15.664775+00:00",
|
||||||
|
"last_used_ip": null,
|
||||||
|
"expire_at": null,
|
||||||
|
"credential_id": null,
|
||||||
|
"version": "2025.12.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "477f4babaa044ef09d2145eb5fc7cf41",
|
||||||
|
"user_id": "9ba78f5ab4e646758f3c093bd79ac9c8",
|
||||||
|
"client_id": "https://home-assistant.io/android",
|
||||||
|
"client_name": null,
|
||||||
|
"client_icon": null,
|
||||||
|
"token_type": "normal",
|
||||||
|
"created_at": "2024-12-23T23:28:01.038637+00:00",
|
||||||
|
"access_token_expiration": 1800.0,
|
||||||
|
"token": "a55121810ab946f004439dbba500e15ffe8862964ef4f7536f3279f451a8c23c3f331d0859893b9bf1fb1c8f28eedc29e28eeb2265c05cb37b1e2203eeab3770",
|
||||||
|
"jwt_key": "19dc7365cc33b800ddd124d9fc04c7af0fcc42c4d67d06265f9b0748f1cb5c6ee5b9c64cb2709b66cca269a3a33240fec98a03b9abd5dafbe6d72c39de973b2e",
|
||||||
|
"last_used_at": "2026-01-29T04:35:39.896016+00:00",
|
||||||
|
"last_used_ip": "127.0.0.1",
|
||||||
|
"expire_at": 1777437339.896016,
|
||||||
|
"credential_id": "5d346781e9be4d4c8324f6b0df61f273",
|
||||||
|
"version": "2024.12.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
10
.storage/auth_module.totp
Normal file
10
.storage/auth_module.totp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "auth_module.totp",
|
||||||
|
"data": {
|
||||||
|
"users": {
|
||||||
|
"4a285b563f6a4a54b657acf1926c0b5b": "GMSADWNDZ45UIQHOHHQUEPXMEQOG7HP3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
.storage/auth_provider.homeassistant
Normal file
17
.storage/auth_provider.homeassistant
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "auth_provider.homeassistant",
|
||||||
|
"data": {
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"username": "cchasteen99",
|
||||||
|
"password": "JDJiJDEyJGtYTDBaT3I0SXFKR3lEdEpRZEx4L2VrenBYS0NzdVlkSlBNUXBtOWtHSE85STdSZUJNeUxD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "gaby",
|
||||||
|
"password": "JDJiJDEyJGVtSkMwOTgzOWFubXhLMWlUL2JtUC45eUtCaFNodENySzhEWnUuV1EudHJWY3paekg4Ny5t"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
54
.storage/backup
Normal file
54
.storage/backup
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 7,
|
||||||
|
"key": "backup",
|
||||||
|
"data": {
|
||||||
|
"backups": [
|
||||||
|
{
|
||||||
|
"backup_id": "83e9a9da",
|
||||||
|
"failed_addons": [],
|
||||||
|
"failed_agent_ids": [],
|
||||||
|
"failed_folders": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"backup_id": "66dc2464",
|
||||||
|
"failed_addons": [],
|
||||||
|
"failed_agent_ids": [],
|
||||||
|
"failed_folders": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"backup_id": "d4c81911",
|
||||||
|
"failed_addons": [],
|
||||||
|
"failed_agent_ids": [],
|
||||||
|
"failed_folders": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"agents": {},
|
||||||
|
"automatic_backups_configured": false,
|
||||||
|
"create_backup": {
|
||||||
|
"agent_ids": [
|
||||||
|
"hassio.local",
|
||||||
|
"cloud.cloud"
|
||||||
|
],
|
||||||
|
"include_addons": [],
|
||||||
|
"include_all_addons": true,
|
||||||
|
"include_database": true,
|
||||||
|
"include_folders": null,
|
||||||
|
"name": null,
|
||||||
|
"password": "D01G-CLSG-9MS5-2J8O-LFID-EE4G-2Z36"
|
||||||
|
},
|
||||||
|
"last_attempted_automatic_backup": "2026-01-30T05:44:31.001585-06:00",
|
||||||
|
"last_completed_automatic_backup": "2026-01-30T05:48:51.732548-06:00",
|
||||||
|
"retention": {
|
||||||
|
"copies": 3,
|
||||||
|
"days": null
|
||||||
|
},
|
||||||
|
"schedule": {
|
||||||
|
"days": [],
|
||||||
|
"recurrence": "daily",
|
||||||
|
"time": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
.storage/bluetooth.passive_update_processor
Normal file
6
.storage/bluetooth.passive_update_processor
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "bluetooth.passive_update_processor",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
10
.storage/camera
Normal file
10
.storage/camera
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "camera",
|
||||||
|
"data": {
|
||||||
|
"camera.catcamfeed": {
|
||||||
|
"preload_stream": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
.storage/cloud
Normal file
71
.storage/cloud
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 4,
|
||||||
|
"key": "cloud",
|
||||||
|
"data": {
|
||||||
|
"alexa_default_expose": [
|
||||||
|
"climate",
|
||||||
|
"cover",
|
||||||
|
"fan",
|
||||||
|
"humidifier",
|
||||||
|
"light",
|
||||||
|
"lock",
|
||||||
|
"scene",
|
||||||
|
"script",
|
||||||
|
"sensor",
|
||||||
|
"switch",
|
||||||
|
"vacuum",
|
||||||
|
"water_heater"
|
||||||
|
],
|
||||||
|
"alexa_entity_configs": {},
|
||||||
|
"alexa_settings_version": 3,
|
||||||
|
"cloud_user": "5f60cc00c4f249e3894b3148caccc231",
|
||||||
|
"cloudhooks": {
|
||||||
|
"bf13749f00cc14d1be9198c33ec3754dadc7c684ec69a5ac06c708cdbce4a20e": {
|
||||||
|
"webhook_id": "bf13749f00cc14d1be9198c33ec3754dadc7c684ec69a5ac06c708cdbce4a20e",
|
||||||
|
"cloudhook_id": "7526246479ee4c2dbf2e0c057a22b2a4",
|
||||||
|
"cloudhook_url": "https://hooks.nabu.casa/gAAAAABnafIBY2GmvegZf3g5cfAL5_TWiRc-ryrGIvcXydAsQn0DKASAUUhVCxc0uri_ochooiy863WV5_9y2eic6eBy631MHy_Efy_FVUnQuJ3LB7pkxJ3E8T7LboGg0vhjz6fKGy1c0iX6CJAtGsKZCx_He8fJIwVztFXB6HWhukLJo6vdZQI=",
|
||||||
|
"managed": true
|
||||||
|
},
|
||||||
|
"73288d9f71a8eaa618fb8a78b3f3e54600ca53eebf8a01b4ce22a3b81640e3cf": {
|
||||||
|
"webhook_id": "73288d9f71a8eaa618fb8a78b3f3e54600ca53eebf8a01b4ce22a3b81640e3cf",
|
||||||
|
"cloudhook_id": "befff0f1fc3749f99b93f1b11f98db97",
|
||||||
|
"cloudhook_url": "https://hooks.nabu.casa/gAAAAABnhH0jyCEgeZgbKT1tTb1wHU0JZyftWTdCEAFTF623sDfroC53AG0dx-CwE-Cl1Voj8Bu3WSODvWIMa-pMv90bJcg-yTNxVhTkFdD_69IZJozer7NcvxCzROdaFz_ZM9tSx2tOWXVzTVgy8CnwmBYpUwaGzxibZK9sBbsG0FqtPsslpCU=",
|
||||||
|
"managed": true
|
||||||
|
},
|
||||||
|
"a308d88905706ccc063aa2ae8aafa5012c3eafc67b0fe95f8aafd77147adc111": {
|
||||||
|
"webhook_id": "a308d88905706ccc063aa2ae8aafa5012c3eafc67b0fe95f8aafd77147adc111",
|
||||||
|
"cloudhook_id": "8ae61e69cac543eb8f3017012f420cc6",
|
||||||
|
"cloudhook_url": "https://hooks.nabu.casa/gAAAAABpQj4_zy_6oerTAckSwqDXSzct0oOHfkICEPBrPut7__ncO0UosZk7v3MUwp7Brm8GQ7FAnRfyvOB9-REPWNIpiYFq1lZF9vfAcuA0cmB2yElVGlwl1mtZqQV2MKmvDRANqPPCWsZjuNIJ6vYFOcoiFcUvBPFQfXFIgG08wRJs7VTO7So=",
|
||||||
|
"managed": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"alexa_enabled": true,
|
||||||
|
"google_enabled": true,
|
||||||
|
"remote_enabled": true,
|
||||||
|
"cloud_ice_servers_enabled": true,
|
||||||
|
"google_connected": true,
|
||||||
|
"google_default_expose": [
|
||||||
|
"climate",
|
||||||
|
"cover",
|
||||||
|
"fan",
|
||||||
|
"humidifier",
|
||||||
|
"light",
|
||||||
|
"lock",
|
||||||
|
"scene",
|
||||||
|
"script",
|
||||||
|
"sensor",
|
||||||
|
"switch",
|
||||||
|
"vacuum",
|
||||||
|
"water_heater"
|
||||||
|
],
|
||||||
|
"google_entity_configs": {},
|
||||||
|
"google_settings_version": 3,
|
||||||
|
"google_local_webhook_id": "54c1728df63c4d56f80caa9c1a88ec925400c37b3a5d5cac3ed26acbe2b8efbc",
|
||||||
|
"instance_id": "c6aa15133c224fb28b425e802909af33",
|
||||||
|
"google_secure_devices_pin": null,
|
||||||
|
"remote_domain": "dlwgsr2kwoovej41itvjkc7cabtymfp3.ui.nabu.casa",
|
||||||
|
"remote_allow_remote_enable": true,
|
||||||
|
"username": "3afe0017-e1eb-4198-abfb-58eded9a84db"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
.storage/core.analytics
Normal file
10
.storage/core.analytics
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "core.analytics",
|
||||||
|
"data": {
|
||||||
|
"onboarded": true,
|
||||||
|
"preferences": {},
|
||||||
|
"uuid": null
|
||||||
|
}
|
||||||
|
}
|
||||||
126
.storage/core.area_registry
Normal file
126
.storage/core.area_registry
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 9,
|
||||||
|
"key": "core.area_registry",
|
||||||
|
"data": {
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": null,
|
||||||
|
"id": "android_shortcuts",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Android Shortcuts",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-23T23:55:08.472924+00:00",
|
||||||
|
"modified_at": "2024-12-23T23:55:08.472926+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": "mdi:bed",
|
||||||
|
"id": "bedroom",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Bedroom",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-02T03:25:15.379030+00:00",
|
||||||
|
"modified_at": "2024-12-23T23:21:01.258001+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": null,
|
||||||
|
"id": "garage",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Garage",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-02T03:27:10.231632+00:00",
|
||||||
|
"modified_at": "2024-12-02T03:27:10.231639+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": null,
|
||||||
|
"id": "groupmembers",
|
||||||
|
"labels": [],
|
||||||
|
"name": "GroupMembers",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-29T05:59:18.684703+00:00",
|
||||||
|
"modified_at": "2024-12-29T05:59:18.684711+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": null,
|
||||||
|
"id": "kitchen",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Kitchen",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-02T03:25:15.379006+00:00",
|
||||||
|
"modified_at": "2024-12-02T03:25:15.379007+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": null,
|
||||||
|
"id": "living_room",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Living Room",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-02T03:25:15.378954+00:00",
|
||||||
|
"modified_at": "2024-12-02T03:25:15.378955+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": "mdi:bathtub",
|
||||||
|
"id": "master_bath",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Master Bath",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-03T04:10:12.010435+00:00",
|
||||||
|
"modified_at": "2024-12-03T04:10:12.010443+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": null,
|
||||||
|
"id": "misc",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Misc",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2024-12-23T23:24:34.611993+00:00",
|
||||||
|
"modified_at": "2024-12-23T23:24:34.611996+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliases": [],
|
||||||
|
"floor_id": null,
|
||||||
|
"humidity_entity_id": null,
|
||||||
|
"icon": null,
|
||||||
|
"id": "unused",
|
||||||
|
"labels": [],
|
||||||
|
"name": "Unused",
|
||||||
|
"picture": null,
|
||||||
|
"temperature_entity_id": null,
|
||||||
|
"created_at": "2025-02-17T05:52:55.864141+00:00",
|
||||||
|
"modified_at": "2025-02-17T05:52:55.864151+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
43
.storage/core.category_registry
Normal file
43
.storage/core.category_registry
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 2,
|
||||||
|
"key": "core.category_registry",
|
||||||
|
"data": {
|
||||||
|
"categories": {
|
||||||
|
"scene": [
|
||||||
|
{
|
||||||
|
"category_id": "01JG8GZ8K9SG3SH5Q5T5DX0DQ3",
|
||||||
|
"created_at": "2024-12-29T06:17:04.873929+00:00",
|
||||||
|
"icon": "mdi:coffin",
|
||||||
|
"modified_at": "2024-12-29T06:17:04.873938+00:00",
|
||||||
|
"name": "z - unused"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category_id": "01JG8GZSHMPFN7HM48G527VR0Y",
|
||||||
|
"created_at": "2024-12-29T06:17:22.228319+00:00",
|
||||||
|
"icon": null,
|
||||||
|
"modified_at": "2024-12-29T06:17:22.228328+00:00",
|
||||||
|
"name": "bedroom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"automation": [
|
||||||
|
{
|
||||||
|
"category_id": "01KAQRTES3MQ0ZR5CFVZKRPEF0",
|
||||||
|
"created_at": "2025-11-23T07:10:59.875598+00:00",
|
||||||
|
"icon": "mdi:bell-cancel",
|
||||||
|
"modified_at": "2025-11-23T07:10:59.875607+00:00",
|
||||||
|
"name": "unmonitored"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"helpers": [
|
||||||
|
{
|
||||||
|
"category_id": "01KFV8JVCQG55VB4C0KD6AWHEB",
|
||||||
|
"created_at": "2026-01-25T19:02:40.279110+00:00",
|
||||||
|
"icon": null,
|
||||||
|
"modified_at": "2026-01-25T19:02:40.279119+00:00",
|
||||||
|
"name": "NH"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
.storage/core.config
Normal file
19
.storage/core.config
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 4,
|
||||||
|
"key": "core.config",
|
||||||
|
"data": {
|
||||||
|
"latitude": 30.2711286,
|
||||||
|
"longitude": -97.7436995,
|
||||||
|
"elevation": 0,
|
||||||
|
"unit_system_v2": "us_customary",
|
||||||
|
"location_name": "Home",
|
||||||
|
"time_zone": "America/Chicago",
|
||||||
|
"external_url": null,
|
||||||
|
"internal_url": "http://192.168.0.107:8123",
|
||||||
|
"currency": "USD",
|
||||||
|
"country": "US",
|
||||||
|
"language": "en",
|
||||||
|
"radius": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
52
.storage/core.config_entries
Normal file
52
.storage/core.config_entries
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 5,
|
||||||
|
"key": "core.config_entries",
|
||||||
|
"data": {
|
||||||
|
"entries": [
|
||||||
|
{"created_at":"2024-12-02T03:23:30.334426+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"sun","entry_id":"01JE2PA14YS95P1MVHDGHSQEB2","minor_version":1,"modified_at":"2024-12-02T03:23:30.334430+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"import","subentries":[],"title":"Sun","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-02T03:23:30.523058+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"hassio","entry_id":"01JE2PA1AVW34BP3R20RJ09CVZ","minor_version":1,"modified_at":"2024-12-02T03:23:30.523060+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"system","subentries":[],"title":"Supervisor","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-02T03:23:30.523216+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"go2rtc","entry_id":"01JE2PA1AV7BK003CH3AYYVXCB","minor_version":1,"modified_at":"2024-12-02T03:23:30.523218+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"system","subentries":[],"title":"go2rtc","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-02T03:24:01.579421+00:00","data":{"integration_created_addon":true,"url":"ws://core-matter-server:5580/ws","use_addon":true},"disabled_by":"user","discovery_keys":{"zeroconf":[{"domain":"zeroconf","key":["_matter._tcp.local.","9BB562B20DADE416-04184D40477BA3E1._matter._tcp.local."],"version":1}]},"domain":"matter","entry_id":"01JE2PAZNB0X2F1Q1EFCC0KVR3","minor_version":1,"modified_at":"2024-12-02T03:24:01.579432+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"zeroconf","subentries":[],"title":"Matter","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-02T03:25:41.074867+00:00","data":{"language":"en","tld":"com"},"disabled_by":null,"discovery_keys":{},"domain":"google_translate","entry_id":"01JE2PE0TJRASHVFFAN0FZQMCB","minor_version":1,"modified_at":"2024-12-02T03:25:41.074869+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"onboarding","subentries":[],"title":"Google Translate text-to-speech","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-02T03:25:41.076780+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"shopping_list","entry_id":"01JE2PE0TMFCS836DRMGVW5Q7X","minor_version":1,"modified_at":"2024-12-02T03:25:41.076782+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"onboarding","subentries":[],"title":"Shopping list","unique_id":"shopping_list","version":1},
|
||||||
|
{"created_at":"2024-12-02T03:25:41.085989+00:00","data":{"track_home":true},"disabled_by":null,"discovery_keys":{},"domain":"met","entry_id":"01JE2PE0TXTYWZGJSBJ1EW6XEF","minor_version":1,"modified_at":"2024-12-02T03:25:41.085992+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"onboarding","subentries":[],"title":"Home","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-02T03:25:41.129580+00:00","data":{},"disabled_by":"user","discovery_keys":{},"domain":"radio_browser","entry_id":"01JE2PE0W9W5S3HFZ4PFK720ZP","minor_version":1,"modified_at":"2024-12-02T03:25:41.129583+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"onboarding","subentries":[],"title":"Radio Browser","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-02T03:57:02.029143+00:00","data":{"device":{"baudrate":115200,"flow_control":null,"path":"/dev/serial/by-id/usb-Itead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_V2_925aeb2ca41fef11bc674ad0639e525b-if00-port0"},"radio_type":"ezsp"},"disabled_by":null,"discovery_keys":{},"domain":"zha","entry_id":"01JE2R7DPDQ8PCN6MMVWNFMDFN","minor_version":1,"modified_at":"2025-11-09T07:27:07.991390+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"usb","subentries":[],"title":"Sonoff Zigbee 3.0 USB Dongle Plus V2","unique_id":"epid=e2:79:42:76:51:67:88:a3","version":5},
|
||||||
|
{"created_at":"2024-12-02T04:23:05.406722+00:00","data":{"token":"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"},"disabled_by":null,"discovery_keys":{},"domain":"hacs","entry_id":"01JE2SQ4DY1D75FFMD9PGMNER0","minor_version":1,"modified_at":"2025-01-08T17:44:25.507697+00:00","options":{"appdaemon":false,"country":"ALL","experimental":true,"sidepanel_icon":"hacs:hacs","sidepanel_title":"HACS"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-13T03:18:32.293923+00:00","data":{"api_key":"30cf6a5c-b6d9-4378-bae9-beb5eca4e5e2","delay":10},"disabled_by":"user","discovery_keys":{},"domain":"govee","entry_id":"01JEZ0CV35V3GN0H3TXQCRZCKH","minor_version":1,"modified_at":"2025-01-08T15:36:13.275027+00:00","options":{"api_key":"30cf6a5c-b6d9-4378-bae9-beb5eca4e5e2","delay":10,"disable_attribute_updates":"","offline_is_off":false,"use_assumed_state":true},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"govee","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-22T02:19:08.905189+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"cloud","entry_id":"01JFP2JJ79E0W23JMRAX0PNWV2","minor_version":1,"modified_at":"2024-12-22T02:19:08.905210+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"system","subentries":[],"title":"Home Assistant Cloud","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-22T03:14:09.105589+00:00","data":{"host":"192.168.0.1","location":"http://192.168.0.1:1900/crjig/rootDesc.xml","mac_address":"40:ed:00:b0:72:a8","original_udn":"uuid:4c80b450-a7bf-432b-a26e-f6767135d368","st":"urn:schemas-upnp-org:device:InternetGatewayDevice:1","udn":"uuid:4c80b450-a7bf-432b-a26e-f6767135d368"},"disabled_by":null,"discovery_keys":{"ssdp":[{"domain":"ssdp","key":"uuid:4c80b450-a7bf-432b-a26e-f6767135d368","version":1}]},"domain":"upnp","entry_id":"01JFP5Q92H27CVE2AF3TK6ZSE4","minor_version":1,"modified_at":"2024-12-22T03:14:09.105595+00:00","options":{"force_poll":false},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"ignore","subentries":[],"title":"Archer AXE75","unique_id":"uuid:4c80b450-a7bf-432b-a26e-f6767135d368::urn:schemas-upnp-org:device:InternetGatewayDevice:1","version":1},
|
||||||
|
{"created_at":"2024-12-22T03:14:11.036716+00:00","data":{"host":"192.168.0.178"},"disabled_by":null,"discovery_keys":{"zeroconf":[{"domain":"zeroconf","key":["_printer._tcp.local.","Brother HL-L3290CDW series._printer._tcp.local."],"version":1}]},"domain":"brother","entry_id":"01JFP5QAYW3YSVDEJFCRQF18PH","minor_version":1,"modified_at":"2024-12-22T08:30:28.457495+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"ignore","subentries":[],"title":"HL-L3290CDW U65177L2N135900","unique_id":"u65177l2n135900","version":1},
|
||||||
|
{"created_at":"2024-12-22T04:14:43.550917+00:00","data":{"password":"idvh3PQTjzAsrx","username":"cchas@att.net"},"disabled_by":null,"discovery_keys":{},"domain":"vesync","entry_id":"01JFP966AYW48GQ7FPY42M80G0","minor_version":2,"modified_at":"2025-09-14T05:33:48.930142+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"cchas@att.net","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-23T21:08:53.009581+00:00","data":{"host":"192.168.0.178","name":"Brother HL-L3290CDW series"},"disabled_by":null,"discovery_keys":{"zeroconf":[{"domain":"zeroconf","key":["_ipp._tcp.local.","Brother HL-L3290CDW series._ipp._tcp.local."],"version":1}]},"domain":"ipp","entry_id":"01JFTNKWMH2NZWD4AZNTAQ1RBF","minor_version":1,"modified_at":"2025-06-18T00:40:07.430257+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"ignore","subentries":[],"title":"Brother HL-L3290CDW series","unique_id":"e3248000-80ce-11db-8000-ac50de540638","version":1},
|
||||||
|
{"created_at":"2024-12-23T23:28:01.327388+00:00","data":{"app_data":{"push_token":"dlY0ndudT9WS0qU5RQFHyA:APA91bFtFZj-urS-AFTCwqMvxi8UT1pC-A5-_Jg5nXiyEvLV_5UJ15fKwsSJ0kxaf4qNA2nWcwrSfvh-H124yYPprFPWWRxs291pzyhfOTWs17XhtsNMsVc","push_url":"https://mobile-apps.home-assistant.io/api/sendPush/android/v1","push_websocket_channel":true},"app_id":"io.homeassistant.companion.android","app_name":"Home Assistant","app_version":"2025.11.4-full (19134)","cloudhook_url":"https://hooks.nabu.casa/gAAAAABnafIBY2GmvegZf3g5cfAL5_TWiRc-ryrGIvcXydAsQn0DKASAUUhVCxc0uri_ochooiy863WV5_9y2eic6eBy631MHy_Efy_FVUnQuJ3LB7pkxJ3E8T7LboGg0vhjz6fKGy1c0iX6CJAtGsKZCx_He8fJIwVztFXB6HWhukLJo6vdZQI=","device_id":"04816a43418fc7ed","device_name":"Gabys Phone","manufacturer":"samsung","model":"SM-G781U","os_name":"Android","os_version":"33","supports_encryption":false,"user_id":"9ba78f5ab4e646758f3c093bd79ac9c8","webhook_id":"bf13749f00cc14d1be9198c33ec3754dadc7c684ec69a5ac06c708cdbce4a20e"},"disabled_by":null,"discovery_keys":{},"domain":"mobile_app","entry_id":"01JFTXJN9FW8ZK5WNKFDK1Y69F","minor_version":1,"modified_at":"2025-11-30T05:40:04.580113+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"registration","subentries":[],"title":"Gabys Phone","unique_id":"io.homeassistant.companion.android-04816a43418fc7ed","version":1},
|
||||||
|
{"created_at":"2024-12-29T05:57:06.238349+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"group","entry_id":"01JG8FTP1Y3ZDPG4QRNTMTM78B","minor_version":1,"modified_at":"2025-04-09T15:53:09.458251+00:00","options":{"all":false,"entities":["light.lamp_upper_light_5","light.lamp_top_light"],"group_type":"light","hide_members":false,"name":"Lamp Lights"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Lamp Lights","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-29T05:57:38.517408+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"group","entry_id":"01JG8FVNJNPSSM4GF1GFC8JR4N","minor_version":1,"modified_at":"2025-04-09T15:45:37.394760+00:00","options":{"all":false,"entities":["light.master_bath_bulb_1_light","light.master_bath_bulb_2_light","light.master_bath_bulb_3_light_4","light.master_bath_bulb_4_light_3"],"group_type":"light","hide_members":false,"name":"Master Bathroom Lights"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Master Bathroom Lights","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2024-12-29T06:00:04.933481+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"group","entry_id":"01JG8G04J5WK0KFJ9T2PYKPCH2","minor_version":1,"modified_at":"2024-12-29T06:00:04.933489+00:00","options":{"entities":["light.bedroom_ceiling_1","light.bedroom_ceiling_2","light.bedroom_ceiling_3","light.bedroom_ceiling_4"],"group_type":"light","hide_members":false,"name":"Bedroom Ceiling Lights"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Bedroom Ceiling Lights","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-01-08T23:42:37.542830+00:00","data":{},"disabled_by":null,"discovery_keys":{"zeroconf":[{"domain":"zeroconf","key":["_googlecast._tcp.local.","Smart-TV-Pro-bb692722b9fab739e50351095f302eaf._googlecast._tcp.local."],"version":1}]},"domain":"cast","entry_id":"01JH44RWZ6B46DWN2WHN4MWBJ9","minor_version":1,"modified_at":"2025-01-08T23:42:37.542849+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"ignore","subentries":[],"title":"cast","unique_id":"cast","version":1},
|
||||||
|
{"created_at":"2025-01-13T02:40:35.981345+00:00","data":{"app_data":{"push_token":"fwjGG3PMS1eL2ABHS1b-ks:APA91bFMsyNgQ1CCWxuopOTSkvBiWLFbXRdLispA2uvie9JG-0HpWWfpH73j5kTis3PD931fwornCgQcOxnNbeJUL90314ttWaT86cM2Cx-uwoPxnFYHbfs","push_url":"https://mobile-apps.home-assistant.io/api/sendPush/android/v1","push_websocket_channel":true},"app_id":"io.homeassistant.companion.android","app_name":"Home Assistant","app_version":"2025.1.2-full (14946)","cloudhook_url":"https://hooks.nabu.casa/gAAAAABnhH0jyCEgeZgbKT1tTb1wHU0JZyftWTdCEAFTF623sDfroC53AG0dx-CwE-Cl1Voj8Bu3WSODvWIMa-pMv90bJcg-yTNxVhTkFdD_69IZJozer7NcvxCzROdaFz_ZM9tSx2tOWXVzTVgy8CnwmBYpUwaGzxibZK9sBbsG0FqtPsslpCU=","device_id":"7d87873685c6e6fb","device_name":"Pixel 3a","manufacturer":"Google","model":"Pixel 3a","os_name":"Android","os_version":"32","supports_encryption":false,"user_id":"4a285b563f6a4a54b657acf1926c0b5b","webhook_id":"73288d9f71a8eaa618fb8a78b3f3e54600ca53eebf8a01b4ce22a3b81640e3cf"},"disabled_by":null,"discovery_keys":{},"domain":"mobile_app","entry_id":"01JHERHN4DSFB0VPVXT2KS52TW","minor_version":1,"modified_at":"2025-01-13T02:40:35.981348+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"registration","subentries":[],"title":"Pixel 3a","unique_id":"io.homeassistant.companion.android-7d87873685c6e6fb","version":1},
|
||||||
|
{"created_at":"2025-01-26T07:14:28.292950+00:00","data":{"auth_implementation":"google_555321766228_fr9p85c52459cgp2d5qbnpkvnh9rha58_apps_googleusercontent_com","credential_type":"web_auth","token":{"access_token":"ya29.a0AUMWg_IsQlP8FeiYvyTC6OkZ2A_yswtXZi3VjF39ovj5PCm_JY8-XJ5MwhkYAA3ogF1oz4p98jPskWs4nM8ZOyEKCllcPznzxcR9DbHGLg_aMgn2bq-05Up5i7h6ipNBqaXseQ7YuXmJIC2g2MlzNLhIY_osQuAAFevwf0CzlhYY2LzowfwgYJznsVnjL8vaMfWdNXHcXwaCgYKAdUSARASFQHGX2MiyqiGSn1vxkz7Ot9e8yO-Wg0209","expires_at":1769837426.4982033,"expires_in":3599,"refresh_token":"1//0f57iGZxHVwYUCgYIARAAGA8SNwF-L9Irq-vJpwdGsu6dOhhVNbp2gEEzv96h8QD2Bt6XYMOIM9zUZPwuuvFR9iAFk4TS6JIuDR4","scope":"https://www.googleapis.com/auth/calendar","token_type":"Bearer"}},"disabled_by":null,"discovery_keys":{},"domain":"google","entry_id":"01JJGQCFA4BSXH2EK2EN1ZD3AA","minor_version":1,"modified_at":"2026-01-31T04:30:27.498280+00:00","options":{"calendar_access":"read_write"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"chastifur@gmail.com","unique_id":"chastifur@gmail.com","version":1},
|
||||||
|
{"created_at":"2025-03-28T19:35:31.169045+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"group","entry_id":"01JQF417309ZB7P11CXKJSE6J0","minor_version":1,"modified_at":"2025-03-28T19:35:31.169061+00:00","options":{"entities":["switch.stair_light_lower_switch","switch.stair_light_upper_switch_2"],"group_type":"switch","hide_members":false,"name":"Stair Group a"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Stair Group a","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-03-28T19:35:49.117769+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"group","entry_id":"01JQF41RKXQK3WPFPRNNA9H9BF","minor_version":1,"modified_at":"2025-10-02T23:46:55.056653+00:00","options":{"all":false,"entities":["switch.upper_landing_a_switch","switch.upper_landing_b_switch"],"group_type":"switch","hide_members":false,"name":"Stair Group b"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Stair Group b","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-04-07T02:38:30.398376+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"wake_on_lan","entry_id":"01JR71T6FYKBW7EQ1SVTRH11BV","minor_version":1,"modified_at":"2025-04-07T02:38:30.398379+00:00","options":{"mac":"9c:6b:00:82:eb:84"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Wake on LAN 9c:6b:00:82:eb:84","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-04-07T03:03:21.792042+00:00","data":{"host":"192.168.0.11","name":"65Q750G"},"disabled_by":null,"discovery_keys":{"zeroconf":[{"domain":"zeroconf","key":["_androidtvremote2._tcp.local.","65Q750G._androidtvremote2._tcp.local."],"version":1}]},"domain":"androidtv_remote","entry_id":"01JR737PY0XGFAG98YEZMWYM5D","minor_version":1,"modified_at":"2025-04-07T21:15:40.507702+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"ignore","subentries":[],"title":"65Q750G","unique_id":"48:9e:9d:98:78:3e","version":1},
|
||||||
|
{"created_at":"2025-04-07T03:04:57.965006+00:00","data":{"device_name":"home-assistant-voice-0981b4","host":"192.168.0.209","noise_psk":"","password":"","port":6053},"disabled_by":null,"discovery_keys":{"dhcp":[{"domain":"dhcp","key":"20f83b0981b4","version":1}],"zeroconf":[{"domain":"zeroconf","key":["_esphomelib._tcp.local.","home-assistant-voice-0981b4._esphomelib._tcp.local."],"version":1}]},"domain":"esphome","entry_id":"01JR73AMVCG4FGGV23MYMXWNJT","minor_version":1,"modified_at":"2025-12-27T05:55:44.923927+00:00","options":{"allow_service_calls":false},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"zeroconf","subentries":[],"title":"Home Assistant Voice 0981b4","unique_id":"20:f8:3b:09:81:b4","version":1},
|
||||||
|
{"created_at":"2025-08-30T04:00:39.092446+00:00","data":{"host":"core-piper","port":10200},"disabled_by":null,"discovery_keys":{"hassio":[{"domain":"hassio","key":"171efec4d2ac41b18bfcdf3d446c78db","version":1}]},"domain":"wyoming","entry_id":"01K3WJ4TNM2DMZ015BXWJZEKQA","minor_version":1,"modified_at":"2025-08-30T04:00:39.092449+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"hassio","subentries":[],"title":"Piper","unique_id":"171efec4d2ac41b18bfcdf3d446c78db","version":1},
|
||||||
|
{"created_at":"2025-08-30T04:01:16.785556+00:00","data":{"host":"core-whisper","port":10300},"disabled_by":"user","discovery_keys":{"hassio":[{"domain":"hassio","key":"9fe3a8ee23c14b3da445da71f3f49246","version":1}]},"domain":"wyoming","entry_id":"01K3WJ5ZFH909FVC2NBKV0PGMY","minor_version":1,"modified_at":"2025-08-30T04:01:16.785568+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"hassio","subentries":[],"title":"Whisper","unique_id":"9fe3a8ee23c14b3da445da71f3f49246","version":1},
|
||||||
|
{"created_at":"2025-09-10T17:55:03.491031+00:00","data":{"host":"core-speech-to-phrase","port":10300},"disabled_by":null,"discovery_keys":{"hassio":[{"domain":"hassio","key":"324ccffd4e294f36a1f86fd7bb1208be","version":1}]},"domain":"wyoming","entry_id":"01K4TC8JP3C7VWBHSSYS4H5HDT","minor_version":1,"modified_at":"2025-09-10T17:55:03.491035+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"hassio","subentries":[],"title":"Speech-to-Phrase","unique_id":"324ccffd4e294f36a1f86fd7bb1208be","version":1},
|
||||||
|
{"created_at":"2025-09-14T05:33:55.068550+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"backup","entry_id":"01K53BECFWDC572ETZWMDH3PG7","minor_version":1,"modified_at":"2025-09-14T05:33:55.068551+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"system","subentries":[],"title":"Backup","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-09-29T01:43:24.300615+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"template","entry_id":"01K69J72WC9WPC7PP9TAJ90315","minor_version":1,"modified_at":"2025-09-29T01:43:24.300619+00:00","options":{"advanced_options":{},"name":"Upper Landing A ","template_type":"switch"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Upper Landing A ","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-10-07T21:58:17.090044+00:00","data":{"auth_implementation":"google_555321766228_fr9p85c52459cgp2d5qbnpkvnh9rha58_apps_googleusercontent_com","credential_type":"web_auth","token":{"access_token":"ya29.a0AUMWg_LC8bICm62e7IpB4TqM2Y0bVbCMC0_8Oj8FtrbygvuvNDD6fYamrbfjmeWKOhZu4s6VkG4TvHZj0uSD52yVag9KgoEdcHpXvpnbhoot_o3Xp4XNoQSWXphIDND7p1GSzGwq3wjWS4-73quE1TMWzfrUnsAsNmgvBCqj5wsJPiL4JVsQWBSv5ThAzXVkwOkHZGAPRwaCgYKAZASARMSFQHGX2MiHmJ8iBwLWkYO2DOFirC0BA0209","expires_at":1769837426.5019999,"expires_in":3599,"refresh_token":"1//0fhn0W37Cuhv-CgYIARAAGA8SNwF-L9IrwJA77btC2T0PH0Kc9AdDJi-R7apg8H9pqvV35apnP2OYDRDFIeQ52jf8uJsVpl71pI0","scope":"https://www.googleapis.com/auth/calendar","token_type":"Bearer"}},"disabled_by":null,"discovery_keys":{},"domain":"google","entry_id":"01K70AXB81Z95CMX993D4Z9K08","minor_version":1,"modified_at":"2026-01-31T04:30:27.502054+00:00","options":{"calendar_access":"read_write"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"chrisrobertchasteen@gmail.com","unique_id":"chrisrobertchasteen@gmail.com","version":1},
|
||||||
|
{"created_at":"2025-11-09T07:16:33.408367+00:00","data":{"password":"pp4wcT67#%rOau","username":"chastifur@gmail.com"},"disabled_by":null,"discovery_keys":{},"domain":"litterrobot","entry_id":"01K9KQJJG0AXR3PCXD8Y3NCHGM","minor_version":1,"modified_at":"2025-11-09T07:16:33.408373+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"chastifur@gmail.com","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-12-13T06:37:44.883815+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"alarmo","entry_id":"01KCB6VYHK4W6DCTV260B9RD3M","minor_version":1,"modified_at":"2025-12-13T06:37:44.883818+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Alarmo","unique_id":"10d15907fccf","version":"1.0.0"},
|
||||||
|
{"created_at":"2025-12-13T07:37:14.695902+00:00","data":{"cloudhook":false,"webhook_id":"76fbd9cba36808425304ca71e4d97bb79d9fc68181086c71068510df1f9cb2a2"},"disabled_by":null,"discovery_keys":{},"domain":"twilio","entry_id":"01KCBA8WP7AW7EBDD3TA6ZTPQV","minor_version":1,"modified_at":"2025-12-13T07:37:14.695903+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Twilio Webhook","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2025-12-17T05:23:11.164165+00:00","data":{"app_data":{"push_token":"fv-M1yFoS6ye6ChUGihRWK:APA91bEhhzD0peUdfKW_0jaf_1sa4BkkcsxcbirCv-Coc12h-AaolEtCxVUcQ4L9WTMv_kHO0_uZMOSspH-CFMqABUyCuvv-nHyq_CUY-6M9s_KcZrY2hrE","push_url":"https://mobile-apps.home-assistant.io/api/sendPush/android/v1","push_websocket_channel":true},"app_id":"io.homeassistant.companion.android","app_name":"Home Assistant","app_version":"2025.11.4-full (19134)","cloudhook_url":"https://hooks.nabu.casa/gAAAAABpQj4_zy_6oerTAckSwqDXSzct0oOHfkICEPBrPut7__ncO0UosZk7v3MUwp7Brm8GQ7FAnRfyvOB9-REPWNIpiYFq1lZF9vfAcuA0cmB2yElVGlwl1mtZqQV2MKmvDRANqPPCWsZjuNIJ6vYFOcoiFcUvBPFQfXFIgG08wRJs7VTO7So=","device_id":"9a80e426cd178688","device_name":"Chris phone ","manufacturer":"OnePlus","model":"CPH2513","os_name":"Android","os_version":"34","supports_encryption":false,"user_id":"4a285b563f6a4a54b657acf1926c0b5b","webhook_id":"a308d88905706ccc063aa2ae8aafa5012c3eafc67b0fe95f8aafd77147adc111"},"disabled_by":null,"discovery_keys":{},"domain":"mobile_app","entry_id":"01KCNC69NWB4NZT8NBA49SEJXS","minor_version":1,"modified_at":"2026-01-01T06:28:45.971434+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"registration","subentries":[],"title":"CPH2513","unique_id":"io.homeassistant.companion.android-9a80e426cd178688","version":1},
|
||||||
|
{"created_at":"2025-12-27T05:27:59.111111+00:00","data":{"device_id":"uuid:37f177fe-ff75-4f3e-a7fb-b976f7ffed67","mac":"08:c3:b3:e6:b1:74","type":"urn:schemas-upnp-org:device:MediaRenderer:1","url":"http://192.168.0.13:16006"},"disabled_by":null,"discovery_keys":{"ssdp":[{"domain":"ssdp","key":"uuid:37f177fe-ff75-4f3e-a7fb-b976f7ffed67","version":1}]},"domain":"dlna_dmr","entry_id":"01KDF4E8W7S74JZ1XETBJ4GHGN","minor_version":1,"modified_at":"2026-01-26T01:54:53.761323+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"ignore","subentries":[],"title":"65Q750G","unique_id":"uuid:37f177fe-ff75-4f3e-a7fb-b976f7ffed67","version":1},
|
||||||
|
{"created_at":"2025-12-28T01:19:03.740660+00:00","data":{},"disabled_by":null,"discovery_keys":{"dhcp":[{"domain":"dhcp","key":"ec71dbf4e5e5","version":1}]},"domain":"reolink","entry_id":"01KDH8K6HWZF40QG05JW36EG2H","minor_version":1,"modified_at":"2025-12-28T01:19:03.740667+00:00","options":{},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"ignore","subentries":[],"title":"cat (192.168.0.37)","unique_id":"ec:71:db:f4:e5:e5","version":1},
|
||||||
|
{"created_at":"2025-12-28T01:19:35.727105+00:00","data":{"baichuan_only":false,"baichuan_port":9000,"firmware_check_time":24386.20798790512,"host":"192.168.0.210","password":"7s8J!G59xOCkY$","port":443,"privacy_mode_supported":true,"use_https":true,"username":"admin"},"disabled_by":null,"discovery_keys":{"dhcp":[{"domain":"dhcp","key":"8c7ab330bb52","version":1}]},"domain":"reolink","entry_id":"01KDH8M5SF0CTB4F4G6TXAP6SZ","minor_version":1,"modified_at":"2026-01-04T08:00:45.377655+00:00","options":{"protocol":"rtsp"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"cat cam 1","unique_id":"8c:7a:b3:30:bb:52","version":1},
|
||||||
|
{"created_at":"2025-12-28T01:30:36.768549+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"generic","entry_id":"01KDH98BB0VPBMK5MYNVK08ZZE","minor_version":1,"modified_at":"2025-12-28T01:55:54.232191+00:00","options":{"authentication":"basic","content_type":"image/jpeg","framerate":15.0,"limit_refetch_to_url_change":false,"rtsp_transport":"tcp","stream_source":"rtsp://admin:7s8J!G59xOCkY$@192.168.0.210:554/h264Preview_01_sub","use_wallclock_as_timestamps":false,"verify_ssl":true},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"192_168_0_210","unique_id":null,"version":1},
|
||||||
|
{"created_at":"2026-01-01T08:13:00.067008+00:00","data":{},"disabled_by":null,"discovery_keys":{},"domain":"derivative","entry_id":"01KDW9W0S2Z43343QVEG0AMT2S","minor_version":4,"modified_at":"2026-01-01T08:13:00.067010+00:00","options":{"name":"Master Bath Humidity","round":2.0,"source":"sensor.third_reality_inc_3rths24bz_humidity","time_window":{"hours":0,"minutes":3,"seconds":0},"unit_time":"min"},"pref_disable_new_entities":false,"pref_disable_polling":false,"source":"user","subentries":[],"title":"Master Bath Humidity","unique_id":null,"version":1}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
119
.storage/core.device_registry
Normal file
119
.storage/core.device_registry
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 12,
|
||||||
|
"key": "core.device_registry",
|
||||||
|
"data": {
|
||||||
|
"devices": [
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA14YS95P1MVHDGHSQEB2"],"config_entries_subentries":{"01JE2PA14YS95P1MVHDGHSQEB2":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T03:23:30.336026+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"14a11eca4be5aa432db00cfbf145e34b","identifiers":[["sun","01JE2PA14YS95P1MVHDGHSQEB2"]],"labels":[],"manufacturer":null,"model":null,"model_id":null,"modified_at":"2024-12-02T03:23:30.336051+00:00","name_by_user":null,"name":"Sun","primary_config_entry":"01JE2PA14YS95P1MVHDGHSQEB2","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T03:23:30.536524+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"b3b64f05673833a7e2c96a3620bcbc51","identifiers":[["hassio","core"]],"labels":[],"manufacturer":"Home Assistant","model":"Home Assistant Core","model_id":null,"modified_at":"2025-12-31T06:44:48.625420+00:00","name_by_user":null,"name":"Home Assistant Core","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"2025.12.5","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T03:23:30.536596+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"48a3f586f66d10f66b59e994e73ec467","identifiers":[["hassio","supervisor"]],"labels":[],"manufacturer":"Home Assistant","model":"Home Assistant Supervisor","model_id":null,"modified_at":"2026-01-31T04:56:02.709870+00:00","name_by_user":null,"name":"Home Assistant Supervisor","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"2026.01.1","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T03:23:30.536629+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"021ffffebb8a7f2ba0188a809dfae075","identifiers":[["hassio","host"]],"labels":[],"manufacturer":"Home Assistant","model":"Home Assistant Host","model_id":null,"modified_at":"2024-12-02T03:23:30.536637+00:00","name_by_user":null,"name":"Home Assistant Host","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T03:23:30.536657+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"0920de3445765a6c5d24f8d79c846900","identifiers":[["hassio","OS"]],"labels":[],"manufacturer":"Home Assistant","model":"Home Assistant Operating System","model_id":null,"modified_at":"2025-11-30T03:24:26.552859+00:00","name_by_user":null,"name":"Home Assistant Operating System","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"16.3","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PE0TXTYWZGJSBJ1EW6XEF"],"config_entries_subentries":{"01JE2PE0TXTYWZGJSBJ1EW6XEF":[null]},"configuration_url":"https://www.met.no/en","connections":[],"created_at":"2024-12-02T03:25:43.733767+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"7745bef489d43140f57b21188c566ad9","identifiers":[["met","01JE2PE0TXTYWZGJSBJ1EW6XEF"]],"labels":[],"manufacturer":"Met.no","model":"Forecast","model_id":null,"modified_at":"2024-12-02T03:25:43.733791+00:00","name_by_user":null,"name":"Forecast","primary_config_entry":"01JE2PE0TXTYWZGJSBJ1EW6XEF","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/core_matter_server","connections":[],"created_at":"2024-12-02T03:28:29.913024+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"4e75e0d7f2057fb7de5bb7f5f76eaaff","identifiers":[["hassio","core_matter_server"]],"labels":[],"manufacturer":"Official add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2026-01-31T04:56:02.709345+00:00","name_by_user":null,"name":"Matter Server","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"8.1.2","via_device_id":null},
|
||||||
|
{"area_id":"living_room","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","7c:c6:b6:ff:fe:49:79:3c"]],"created_at":"2024-12-02T03:57:05.804714+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"a6b0311faa8ba235909a9316b2313cc8","identifiers":[["zha","7c:c6:b6:ff:fe:49:79:3c"]],"labels":[],"manufacturer":"","model":"Generic Zigbee Coordinator (EZSP)","model_id":null,"modified_at":"2025-12-31T06:45:29.435097+00:00","name_by_user":"Zigbee Stick","name":" Generic Zigbee Coordinator (EZSP)","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:0a:f3:89:88"]],"created_at":"2024-12-02T04:00:22.585818+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"ebab4eee8659e99e53cb945fa322cde2","identifiers":[["zha","00:15:8d:00:0a:f3:89:88"]],"labels":["monitored"],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-12-31T06:29:19.872572+00:00","name_by_user":"Bedroom Bed Button","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs","connections":[],"created_at":"2024-12-02T04:23:07.671714+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"33e6090fad1c7f4b0c6e9a04d973e531","identifiers":[["hacs","0717a0cd-745c-48fd-9b16-c8534c9704f9-bc944b0f-fd42-4a58-a072-ade38d1444cd"]],"labels":[],"manufacturer":"hacs.xyz","model":"","model_id":null,"modified_at":"2025-02-15T22:18:20.149511+00:00","name_by_user":null,"name":"HACS","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":"2.0.5","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/312080478","connections":[],"created_at":"2024-12-02T04:24:44.267192+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"250233086672d987efd142a6d3c01b20","identifiers":[["hacs","312080478"]],"labels":[],"manufacturer":"LaggAt","model":"integration","model_id":null,"modified_at":"2024-12-02T04:24:44.267239+00:00","name_by_user":null,"name":"govee","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"master_bath","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:0a:f4:5c:83"]],"created_at":"2024-12-03T02:15:03.183633+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"984d2d9da0ce0b58b3ab4649bb82cb09","identifiers":[["zha","00:15:8d:00:0a:f4:5c:83"]],"labels":["monitored"],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-11-23T07:22:10.891292+00:00","name_by_user":"MasterBathButton2","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:0a:f3:b9:b8"]],"created_at":"2024-12-04T03:47:03.947001+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"0e833fe5953d72d36def81f42ddec2dd","identifiers":[["zha","00:15:8d:00:0a:f3:b9:b8"]],"labels":["monitored"],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-11-23T07:22:10.904413+00:00","name_by_user":"Master Bath Outer","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JEZ0CV35V3GN0H3TXQCRZCKH"],"config_entries_subentries":{"01JEZ0CV35V3GN0H3TXQCRZCKH":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T04:29:24.058659+00:00","disabled_by":"config_entry","entry_type":null,"hw_version":null,"id":"69c8478efdc84520141b9030fad78d99","identifiers":[["govee","govee_govee_B6:FE:D4:AD:FC:1F:CE:A2"]],"labels":[],"manufacturer":"Govee","model":"H5080","model_id":null,"modified_at":"2025-09-20T23:14:34.015785+00:00","name_by_user":"govee plug 1","name":"Fan","primary_config_entry":"01JEZ0CV35V3GN0H3TXQCRZCKH","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JEZ0CV35V3GN0H3TXQCRZCKH"],"config_entries_subentries":{"01JEZ0CV35V3GN0H3TXQCRZCKH":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T04:29:24.061888+00:00","disabled_by":"config_entry","entry_type":null,"hw_version":null,"id":"18d8945de4c9dd9ed60853bb73427f9d","identifiers":[["govee","govee_govee_3B:D9:D4:AD:FC:1F:E2:40"]],"labels":[],"manufacturer":"Govee","model":"H5080","model_id":null,"modified_at":"2025-09-20T23:14:34.016488+00:00","name_by_user":null,"name":"Fountain Plug","primary_config_entry":"01JEZ0CV35V3GN0H3TXQCRZCKH","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JEZ0CV35V3GN0H3TXQCRZCKH"],"config_entries_subentries":{"01JEZ0CV35V3GN0H3TXQCRZCKH":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-02T04:29:24.063917+00:00","disabled_by":"config_entry","entry_type":null,"hw_version":null,"id":"191572e88f483df7e49e6a07231c6ec0","identifiers":[["govee","govee_govee_91:99:A4:C1:38:81:F9:86"]],"labels":[],"manufacturer":"Govee","model":"H6110","model_id":null,"modified_at":"2025-09-20T23:14:34.016864+00:00","name_by_user":null,"name":"Bookcase","primary_config_entry":"01JEZ0CV35V3GN0H3TXQCRZCKH","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JEZ0CV35V3GN0H3TXQCRZCKH"],"config_entries_subentries":{"01JEZ0CV35V3GN0H3TXQCRZCKH":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-13T03:18:33.370343+00:00","disabled_by":"config_entry","entry_type":null,"hw_version":null,"id":"4b32c38fbef38b529871c154a37baca8","identifiers":[["govee","govee_govee_8A:D1:D0:C9:07:A9:FD:52"]],"labels":[],"manufacturer":"Govee","model":"H5080","model_id":null,"modified_at":"2025-09-20T23:14:34.017129+00:00","name_by_user":null,"name":"Smart Plug 3","primary_config_entry":"01JEZ0CV35V3GN0H3TXQCRZCKH","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JEZ0CV35V3GN0H3TXQCRZCKH"],"config_entries_subentries":{"01JEZ0CV35V3GN0H3TXQCRZCKH":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-13T03:18:33.370897+00:00","disabled_by":"config_entry","entry_type":null,"hw_version":null,"id":"8a9c147b67efe0deac968f2dbbc65f8f","identifiers":[["govee","govee_govee_D9:42:D0:C9:07:A7:85:9C"]],"labels":[],"manufacturer":"Govee","model":"H5080","model_id":null,"modified_at":"2025-09-20T23:14:34.017363+00:00","name_by_user":null,"name":"Smart Plug 4","primary_config_entry":"01JEZ0CV35V3GN0H3TXQCRZCKH","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JEZ0CV35V3GN0H3TXQCRZCKH"],"config_entries_subentries":{"01JEZ0CV35V3GN0H3TXQCRZCKH":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-13T03:18:33.371417+00:00","disabled_by":"config_entry","entry_type":null,"hw_version":null,"id":"fb0050754cf384566c825998315a6121","identifiers":[["govee","govee_govee_D1:2F:D0:C9:07:B3:01:E6"]],"labels":[],"manufacturer":"Govee","model":"H5080","model_id":null,"modified_at":"2025-09-20T23:14:34.017585+00:00","name_by_user":null,"name":"Smart Plug 5","primary_config_entry":"01JEZ0CV35V3GN0H3TXQCRZCKH","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JEZ0CV35V3GN0H3TXQCRZCKH"],"config_entries_subentries":{"01JEZ0CV35V3GN0H3TXQCRZCKH":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-13T03:18:33.371931+00:00","disabled_by":"config_entry","entry_type":null,"hw_version":null,"id":"7b2c2a46b6149ba52fccbd8f76da9448","identifiers":[["govee","govee_govee_C9:80:D0:C9:07:98:6F:0A"]],"labels":[],"manufacturer":"Govee","model":"H5080","model_id":null,"modified_at":"2025-09-20T23:14:34.017849+00:00","name_by_user":null,"name":"Smart Plug 6","primary_config_entry":"01JEZ0CV35V3GN0H3TXQCRZCKH","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ec:57:97"]],"created_at":"2024-12-16T01:50:17.079878+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"d8478de5f4ea431ada272ac28ad7a2a0","identifiers":[["zha","28:2c:02:bf:ff:ec:57:97"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSP019BZ","model_id":null,"modified_at":"2025-12-31T06:30:16.252102+00:00","name_by_user":"Bedroom Corner Fan","name":"Third Reality, Inc 3RSP019BZ","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10013065","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ec:29:f7"]],"created_at":"2024-12-16T01:50:19.123802+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"886a5ef4c95ee2fa22bbfe49009e8dc7","identifiers":[["zha","28:2c:02:bf:ff:ec:29:f7"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSP019BZ","model_id":null,"modified_at":"2025-12-09T15:48:16.753486+00:00","name_by_user":"Hallway Plug","name":"Third Reality, Inc 3RSP019BZ","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10013065","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"garage","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ec:75:e7"]],"created_at":"2024-12-16T01:51:34.894442+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"8e17de57e7c9665e843c590b6571d6a6","identifiers":[["zha","28:2c:02:bf:ff:ec:75:e7"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSP019BZ","model_id":null,"modified_at":"2025-12-09T15:45:34.020298+00:00","name_by_user":"3d printer light","name":"Third Reality, Inc 3RSP019BZ","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10013065","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ec:5b:d5"]],"created_at":"2024-12-16T01:51:36.865113+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"8860a33d7f6298f79282948f52d3b5db","identifiers":[["zha","28:2c:02:bf:ff:ec:5b:d5"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RSP019BZ","model_id":null,"modified_at":"2025-09-14T05:33:54.406091+00:00","name_by_user":"Office Fan Plug","name":"Third Reality, Inc 3RSP019BZ","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x1001305c","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01KCNC69NWB4NZT8NBA49SEJXS"],"config_entries_subentries":{"01KCNC69NWB4NZT8NBA49SEJXS":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-22T00:24:53.645149+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"7fbfcf84ce84cf89811ed041532155f4","identifiers":[["mobile_app","9a80e426cd178688"]],"labels":[],"manufacturer":"OnePlus","model":"CPH2513","model_id":null,"modified_at":"2026-01-01T06:28:45.970719+00:00","name_by_user":null,"name":"Chris phone ","primary_config_entry":"01KCNC69NWB4NZT8NBA49SEJXS","serial_number":null,"sw_version":"34","via_device_id":null},
|
||||||
|
{"area_id":"misc","config_entries":["01JFP966AYW48GQ7FPY42M80G0"],"config_entries_subentries":{"01JFP966AYW48GQ7FPY42M80G0":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-22T04:14:44.867415+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"e80267ac3e329caa3f04e7f7948cb477","identifiers":[["vesync","vsaq52505b449159ce3821f41cda96a7"]],"labels":[],"manufacturer":"VeSync","model":"Core200S","model_id":null,"modified_at":"2025-11-09T07:27:10.565256+00:00","name_by_user":null,"name":"bedroom air purifier","primary_config_entry":"01JFP966AYW48GQ7FPY42M80G0","serial_number":null,"sw_version":"1.1.15","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JFP966AYW48GQ7FPY42M80G0"],"config_entries_subentries":{"01JFP966AYW48GQ7FPY42M80G0":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-22T04:14:44.870987+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"e8b15c69099402eabeaf4488b6239a92","identifiers":[["vesync","vsaqca39a5b4bf2b562ba86b5f49a916"]],"labels":[],"manufacturer":"VeSync","model":"LAP-V102S-WUS","model_id":null,"modified_at":"2025-11-09T07:27:10.564932+00:00","name_by_user":null,"name":"Vital 100S","primary_config_entry":"01JFP966AYW48GQ7FPY42M80G0","serial_number":null,"sw_version":"1.0.08","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JFTXJN9FW8ZK5WNKFDK1Y69F"],"config_entries_subentries":{"01JFTXJN9FW8ZK5WNKFDK1Y69F":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-23T23:28:01.327466+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"efa17782a42adb5af7f2cfafe8573719","identifiers":[["mobile_app","04816a43418fc7ed"]],"labels":[],"manufacturer":"samsung","model":"SM-G781U","model_id":null,"modified_at":"2024-12-23T23:28:01.327483+00:00","name_by_user":null,"name":"Gabys Phone","primary_config_entry":"01JFTXJN9FW8ZK5WNKFDK1Y69F","serial_number":null,"sw_version":"33","via_device_id":null},
|
||||||
|
{"area_id":"groupmembers","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ea:9c:af"]],"created_at":"2024-12-28T23:17:39.673298+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"ced4292fbcbaa682b7971380652ca751","identifiers":[["zha","28:2c:02:bf:ff:ea:9c:af"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-12-31T06:33:26.019376+00:00","name_by_user":"upper landing Office Adjacent","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"groupmembers","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ea:a3:22"]],"created_at":"2024-12-28T23:17:49.936083+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"9f3ed8a617d6da4c6eb0a2fd412deba4","identifiers":[["zha","28:2c:02:bf:ff:ea:a3:22"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-12-31T06:33:19.276927+00:00","name_by_user":"Stair Light upper","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ea:98:c3"]],"created_at":"2024-12-28T23:18:00.262083+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"09112e3aa200fc6c51b9d8ef8cdd1986","identifiers":[["zha","28:2c:02:bf:ff:ea:98:c3"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-11-23T07:22:10.902730+00:00","name_by_user":"Bedroom Ceiling Lights","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ea:9c:9a"]],"created_at":"2024-12-28T23:18:45.297594+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"63701be14b86c1fe10d30a39b5e4b768","identifiers":[["zha","28:2c:02:bf:ff:ea:9c:9a"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-12-31T06:29:56.031433+00:00","name_by_user":"Bedroom ceiling fan","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"living_room","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","f4:42:50:c3:7d:ff:00:00"]],"created_at":"2025-01-07T03:02:00.494016+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"5bc89d5269cf97fcdff11311559cc80d","identifiers":[["zha","f4:42:50:c3:7d:ff:00:00"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSNL02043Z","model_id":null,"modified_at":"2025-11-23T07:22:10.892286+00:00","name_by_user":"Night Light 1","name":"Third Reality, Inc 3RSNL02043Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000052","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/680112919","connections":[],"created_at":"2025-01-08T05:02:51.448263+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"7006cf694f7f000ffcd5237cb314a3a7","identifiers":[["hacs","680112919"]],"labels":[],"manufacturer":"Clooos","model":"plugin","model_id":null,"modified_at":"2025-01-08T05:02:51.448283+00:00","name_by_user":null,"name":"Bubble Card","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"groupmembers","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ee:7f:18"]],"created_at":"2025-01-09T01:52:43.122189+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"ee6192c8e7833df8a2b4e3ce759be2b5","identifiers":[["zha","28:2c:02:bf:ff:ee:7f:18"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-12-31T06:33:12.489804+00:00","name_by_user":"Stair Light lower","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ee:8f:fb"]],"created_at":"2025-01-09T01:53:42.217469+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"2b036805d62affb4e7933ee034279bad","identifiers":[["zha","28:2c:02:bf:ff:ee:8f:fb"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-11-23T07:22:10.903560+00:00","name_by_user":"Office Ceiling Fan","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JHERHN4DSFB0VPVXT2KS52TW"],"config_entries_subentries":{"01JHERHN4DSFB0VPVXT2KS52TW":[null]},"configuration_url":null,"connections":[],"created_at":"2025-01-13T02:40:35.981458+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"881187d0e75ff02bf021dd5f225e47cc","identifiers":[["mobile_app","7d87873685c6e6fb"]],"labels":[],"manufacturer":"Google","model":"Pixel 3a","model_id":null,"modified_at":"2025-01-13T02:40:35.981487+00:00","name_by_user":null,"name":"Pixel 3a","primary_config_entry":"01JHERHN4DSFB0VPVXT2KS52TW","serial_number":null,"sw_version":"32","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/497319128","connections":[],"created_at":"2025-01-13T03:24:27.485120+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"6d9cbeece1a8412e5985599c7b3acb21","identifiers":[["hacs","497319128"]],"labels":[],"manufacturer":"NemesisRE","model":"plugin","model_id":null,"modified_at":"2025-01-13T03:24:27.485162+00:00","name_by_user":null,"name":"Kiosk Mode","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"living_room","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","a4:c1:38:72:67:f9:5d:d1"]],"created_at":"2025-01-13T23:08:45.204271+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"4b17e41fc231eb5e696ef7c63c724a38","identifiers":[["zha","a4:c1:38:72:67:f9:5d:d1"]],"labels":["monitored"],"manufacturer":"eWeLight","model":"ZB-CL01","model_id":null,"modified_at":"2025-12-31T06:33:50.446801+00:00","name_by_user":"Livingroom FigureCase","name":"eWeLight ZB-CL01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"living_room","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","a4:c1:38:26:5d:39:64:49"]],"created_at":"2025-01-18T02:06:05.603198+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"f3e8dcade53ac6843fde79376c099a3c","identifiers":[["zha","a4:c1:38:26:5d:39:64:49"]],"labels":["monitored"],"manufacturer":"eWeLight","model":"ZB-CL01","model_id":null,"modified_at":"2025-11-23T07:22:10.893509+00:00","name_by_user":"Movie case right","name":"eWeLight ZB-CL01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:0a:f3:60:52"]],"created_at":"2025-01-25T00:20:07.191278+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"052358b767ecf6b2609acf2fe20f7319","identifiers":[["zha","00:15:8d:00:0a:f3:60:52"]],"labels":["monitored"],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-12-31T06:30:44.955554+00:00","name_by_user":"Bedroom doorway button","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/core_configurator","connections":[],"created_at":"2025-01-27T00:05:47.402413+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"f77d5455b3eccf6f811be5fa572a9808","identifiers":[["hassio","core_configurator"]],"labels":[],"manufacturer":"Official add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2025-01-27T00:05:47.402458+00:00","name_by_user":null,"name":"File editor","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"5.8.0","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/737780218","connections":[],"created_at":"2025-01-27T19:08:57.600690+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"0fcf2a08555414de170d3c21b9ab71e1","identifiers":[["hacs","737780218"]],"labels":[],"manufacturer":"elchininet","model":"plugin","model_id":null,"modified_at":"2025-01-27T19:08:57.600797+00:00","name_by_user":null,"name":"Custom Sidebar","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:8b:7b:7b:21"]],"created_at":"2025-02-05T20:33:34.377944+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"47f0f4d6680cc047c758ff166b1d214c","identifiers":[["zha","00:15:8d:00:8b:7b:7b:21"]],"labels":[],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-02-15T22:45:17.589420+00:00","name_by_user":"aqara 7","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:8b:7b:7a:4d"]],"created_at":"2025-02-05T20:40:43.476307+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"4fb39838d69748ace7b3ed8c3d917814","identifiers":[["zha","00:15:8d:00:8b:7b:7a:4d"]],"labels":[],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-02-15T22:45:17.589885+00:00","name_by_user":"aqara 10","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:8b:7b:7b:7e"]],"created_at":"2025-02-05T20:41:21.692531+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"9903b7ebb3846207e70fd2b4137696f9","identifiers":[["zha","00:15:8d:00:8b:7b:7b:7e"]],"labels":[],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-02-15T22:45:17.590374+00:00","name_by_user":"aqara 8","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:8b:7d:19:5b"]],"created_at":"2025-02-05T20:42:11.785552+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"a5b95319cae01f36d393a7ef03dca7d6","identifiers":[["zha","00:15:8d:00:8b:7d:19:5b"]],"labels":[],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-02-15T22:45:17.590851+00:00","name_by_user":"aqara 9","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:8b:7d:16:b4"]],"created_at":"2025-02-05T20:44:41.684423+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"3126ead552545da0409ab0f342d6fa54","identifiers":[["zha","00:15:8d:00:8b:7d:16:b4"]],"labels":[],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-02-15T22:45:17.591413+00:00","name_by_user":"aqara 11","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:8b:7b:7c:8a"]],"created_at":"2025-02-05T20:45:17.381719+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"bef801c7731e52758cc4a92a3dcceb02","identifiers":[["zha","00:15:8d:00:8b:7b:7c:8a"]],"labels":[],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-02-15T22:45:17.591943+00:00","name_by_user":"aqara 12","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","00:15:8d:00:8b:7b:73:d9"]],"created_at":"2025-02-05T20:47:16.180712+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"fbc1b1f2012dd58ab3ff7a99d9433a80","identifiers":[["zha","00:15:8d:00:8b:7b:73:d9"]],"labels":[],"manufacturer":"LUMI","model":"lumi.remote.b1acn01","model_id":null,"modified_at":"2025-02-15T22:45:17.592456+00:00","name_by_user":"aqara 6","name":"LUMI lumi.remote.b1acn01","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":null,"via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","a4:c1:38:74:a6:df:92:66"]],"created_at":"2025-02-06T01:18:41.625036+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"bad245c273e380f605b53cf12e4a323e","identifiers":[["zha","a4:c1:38:74:a6:df:92:66"]],"labels":["monitored","kitchen_sink"],"manufacturer":"eWeLink","model":"SNZB-03","model_id":null,"modified_at":"2025-11-23T07:22:10.906060+00:00","name_by_user":"Leak sensor 1","name":"eWeLink SNZB-03","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10033607","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","a4:c1:38:f3:1f:0c:63:b0"]],"created_at":"2025-02-06T01:22:34.714078+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"1ed578dcc765852dc917e7331a8e8899","identifiers":[["zha","a4:c1:38:f3:1f:0c:63:b0"]],"labels":["downstair_bathroom_sink","monitored"],"manufacturer":"eWeLink","model":"SNZB-03","model_id":null,"modified_at":"2025-11-23T07:22:10.906615+00:00","name_by_user":"Leak sensor 2","name":"eWeLink SNZB-03","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10033607","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","a4:c1:38:f1:2f:da:9c:85"]],"created_at":"2025-02-06T01:26:57.849668+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"f8a00c0fd21488da42cb95ef06c2e278","identifiers":[["zha","a4:c1:38:f1:2f:da:9c:85"]],"labels":["monitored","officesink"],"manufacturer":"eWeLink","model":"SNZB-03","model_id":null,"modified_at":"2025-11-23T07:22:10.907038+00:00","name_by_user":"Leak sensor 3","name":"eWeLink SNZB-03","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10033607","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","a4:c1:38:91:6b:af:67:12"]],"created_at":"2025-02-06T01:28:39.986430+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"3b6c02595d52f95b6d9143d0588c9bce","identifiers":[["zha","a4:c1:38:91:6b:af:67:12"]],"labels":["monitored","bedroomsink"],"manufacturer":"eWeLink","model":"SNZB-03","model_id":null,"modified_at":"2025-11-23T07:22:10.907434+00:00","name_by_user":"Leak sensor 4","name":"eWeLink SNZB-03","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10033607","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","a4:c1:38:4f:93:24:ec:3e"]],"created_at":"2025-02-15T22:10:58.517976+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"235a167cfb925bbe6127061eba485382","identifiers":[["zha","a4:c1:38:4f:93:24:ec:3e"]],"labels":["monitored","waterheater"],"manufacturer":"eWeLink","model":"SNZB-03","model_id":null,"modified_at":"2025-11-23T07:22:10.907769+00:00","name_by_user":"Leak Sensor 5","name":"eWeLink SNZB-03","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x10033607","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JFP966AYW48GQ7FPY42M80G0"],"config_entries_subentries":{"01JFP966AYW48GQ7FPY42M80G0":[null]},"configuration_url":null,"connections":[],"created_at":"2025-02-15T22:45:14.921569+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"25f9137077e6f1c2948f2cd348f693e9","identifiers":[["vesync","vsaq0f17b224937815db3db7f8f2cc4a"]],"labels":[],"manufacturer":"VeSync","model":"Dual200S","model_id":null,"modified_at":"2025-02-15T22:45:14.921611+00:00","name_by_user":null,"name":"bedroom moisturize me ","primary_config_entry":"01JFP966AYW48GQ7FPY42M80G0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/249381778","connections":[],"created_at":"2025-02-16T21:59:20.912695+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"c0351a3de6c8d846f71ead1c49874982","identifiers":[["hacs","249381778"]],"labels":[],"manufacturer":"rospogrigio, postlund","model":"integration","model_id":null,"modified_at":"2025-02-16T21:59:20.912797+00:00","name_by_user":null,"name":"Local Tuya","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"groupmembers","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","28:2c:02:bf:ff:ee:8f:0b"]],"created_at":"2025-03-28T19:27:57.605769+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"ce0a9c0f70a7cddd92616d63aacd4270","identifiers":[["zha","28:2c:02:bf:ff:ee:8f:0b"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-12-09T16:07:04.952788+00:00","name_by_user":"Upper Landing Stair Adjacent","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"living_room","config_entries":["01JR71T6FYKBW7EQ1SVTRH11BV"],"config_entries_subentries":{"01JR71T6FYKBW7EQ1SVTRH11BV":[null]},"configuration_url":null,"connections":[["mac","9c:6b:00:82:eb:84"]],"created_at":"2025-04-07T02:38:30.400489+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"19133a3a1631081f9cde1dfdce814289","identifiers":[],"labels":[],"manufacturer":null,"model":null,"model_id":null,"modified_at":"2025-04-07T02:39:00.374507+00:00","name_by_user":"Wake The Cats Eye","name":"Wake on LAN 9c:6b:00:82:eb:84","primary_config_entry":null,"serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JR73AMVCG4FGGV23MYMXWNJT"],"config_entries_subentries":{"01JR73AMVCG4FGGV23MYMXWNJT":[null]},"configuration_url":null,"connections":[["mac","20:f8:3b:09:81:b4"]],"created_at":"2025-02-07T20:24:54.923774+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"a8e9bb3083a61502f9f45102de706b89","identifiers":[],"labels":[],"manufacturer":"Nabu Casa","model":"Home Assistant Voice PE","model_id":null,"modified_at":"2025-08-30T03:59:17.565996+00:00","name_by_user":null,"name":"Home Assistant Voice 0981b4","primary_config_entry":"01JR73AMVCG4FGGV23MYMXWNJT","serial_number":null,"sw_version":"25.6.0 (ESPHome 2025.6.2)","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","7c:b9:4c:6b:a4:52:00:00"]],"created_at":"2025-04-09T15:41:44.724363+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"e5d02288501bd627adeb3f0c3f0cd438","identifiers":[["zha","7c:b9:4c:6b:a4:52:00:00"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-11-23T07:22:10.908085+00:00","name_by_user":"Master bath bulb 1","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","7c:b9:4c:6b:a1:47:00:00"]],"created_at":"2025-04-09T15:41:47.513569+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"781d88d0750fab7b6f31d0768bfd0401","identifiers":[["zha","7c:b9:4c:6b:a1:47:00:00"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-09-14T05:33:54.407017+00:00","name_by_user":"Lamp lower","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","7c:b9:4c:63:2d:e7:00:00"]],"created_at":"2025-04-09T15:42:00.063114+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"7007113e562ec2dcaec9bc58c572aaa2","identifiers":[["zha","7c:b9:4c:63:2d:e7:00:00"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-11-23T07:22:10.908419+00:00","name_by_user":"Master bath bulb 4","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","b4:e8:42:86:5f:60:00:00"]],"created_at":"2025-04-09T15:42:08.430328+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"9201a4b298d275159a1b7a76601ede56","identifiers":[["zha","b4:e8:42:86:5f:60:00:00"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-11-23T07:22:10.908729+00:00","name_by_user":"Master bath bulb 3","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","b4:e8:42:86:55:8c:00:00"]],"created_at":"2025-04-09T15:42:08.622807+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"4ed9228b5d70e67ea576f4d0527b2437","identifiers":[["zha","b4:e8:42:86:55:8c:00:00"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-09-14T05:33:54.407161+00:00","name_by_user":"Lamp upper","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","7c:b9:4c:6b:9f:db:00:00"]],"created_at":"2025-04-09T15:42:12.442627+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"8da72f490818d47c97c324d00612c391","identifiers":[["zha","7c:b9:4c:6b:9f:db:00:00"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-11-23T07:22:10.909008+00:00","name_by_user":"Master bath bulb 2","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","7c:b9:4c:63:20:cc:00:00"]],"created_at":"2025-04-09T15:52:29.600851+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"a8b279dc583333b6f848bb719cd9370d","identifiers":[["zha","7c:b9:4c:63:20:cc:00:00"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-09-14T05:33:54.407254+00:00","name_by_user":"Lamp top","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","7c:b9:4c:78:53:8e:00:00"]],"created_at":"2025-04-09T15:57:10.886890+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"047a342c6f6709cd88b1ce4f32c13051","identifiers":[["zha","7c:b9:4c:78:53:8e:00:00"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RCB01057Z","model_id":null,"modified_at":"2025-09-14T05:33:54.407311+00:00","name_by_user":"Table light","name":"Third Reality, Inc 3RCB01057Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000038","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/core_piper","connections":[],"created_at":"2025-08-30T04:01:17.726900+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"a6f4fcf9773a9ebea1712108ffb237bd","identifiers":[["hassio","core_piper"]],"labels":[],"manufacturer":"Official add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2025-12-13T07:33:54.012014+00:00","name_by_user":null,"name":"Piper","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"2.1.1","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/core_whisper","connections":[],"created_at":"2025-08-30T04:01:17.727348+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"ae44276fcaa8b2957fe65fa4fad449b4","identifiers":[["hassio","core_whisper"]],"labels":[],"manufacturer":"Official add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2025-12-13T07:33:54.012066+00:00","name_by_user":null,"name":"Whisper","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"3.0.1","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/core_speech-to-phrase","connections":[],"created_at":"2025-09-10T17:56:17.577314+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"ce0db12aef730bce65d43ef3fc259d29","identifiers":[["hassio","core_speech-to-phrase"]],"labels":[],"manufacturer":"Official add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2025-09-10T17:56:17.577487+00:00","name_by_user":null,"name":"Speech-to-Phrase","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"1.4.1","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01K53BECFWDC572ETZWMDH3PG7"],"config_entries_subentries":{"01K53BECFWDC572ETZWMDH3PG7":[null]},"configuration_url":"homeassistant://config/backup","connections":[],"created_at":"2025-09-14T05:33:55.078922+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"1921e82735fa1585aedb4492bb5938b3","identifiers":[["backup","backup_manager"]],"labels":[],"manufacturer":"Home Assistant","model":"Home Assistant Backup","model_id":null,"modified_at":"2025-12-31T06:44:49.115426+00:00","name_by_user":null,"name":"Backup","primary_config_entry":"01K53BECFWDC572ETZWMDH3PG7","serial_number":null,"sw_version":"2025.12.5","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/d5369777_music_assistant","connections":[],"created_at":"2025-11-03T00:08:43.131725+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"b78f9c386eeb09f789d187fd42a9788c","identifiers":[["hassio","d5369777_music_assistant"]],"labels":[],"manufacturer":"Music Assistant","model":"Home Assistant Add-on","model_id":null,"modified_at":"2026-01-31T04:56:02.709569+00:00","name_by_user":null,"name":"Music Assistant","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"2.7.2","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/2a018ee8_squeezelite","connections":[],"created_at":"2025-11-04T02:53:43.127497+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"5e0251a30811df242ea2fe5fc1228fcd","identifiers":[["hassio","2a018ee8_squeezelite"]],"labels":[],"manufacturer":"Squeezelite for Logitech Media Server Addon for Home Assistant","model":"Home Assistant Add-on","model_id":null,"modified_at":"2025-11-04T02:53:43.127512+00:00","name_by_user":null,"name":"Squeezelite","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"0.0.21","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/a0d7b954_spotify","connections":[],"created_at":"2025-11-04T03:28:42.774361+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"375aa9e2465c298b7dfd40aadd1e8751","identifiers":[["hassio","a0d7b954_spotify"]],"labels":[],"manufacturer":"Home Assistant Community Add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2025-12-13T07:33:54.012159+00:00","name_by_user":null,"name":"Spotify Connect","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"0.17.0","via_device_id":null},
|
||||||
|
{"area_id":"living_room","config_entries":["01K9KQJJG0AXR3PCXD8Y3NCHGM"],"config_entries_subentries":{"01K9KQJJG0AXR3PCXD8Y3NCHGM":[null]},"configuration_url":null,"connections":[],"created_at":"2024-12-29T05:50:44.020063+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"9d81266973e5ff00a4e9f974e8b3b621","identifiers":[["litterrobot","LR4C187718"]],"labels":[],"manufacturer":"Whisker","model":"Litter-Robot 4","model_id":null,"modified_at":"2025-11-09T07:27:10.925070+00:00","name_by_user":null,"name":"Litter-Robot 4","primary_config_entry":"01K9KQJJG0AXR3PCXD8Y3NCHGM","serial_number":"LR4C187718","sw_version":"ESP: 1.1.75 / PIC: 10500.3072.2.93 / TOF: 5.0.2.1","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01K9KQJJG0AXR3PCXD8Y3NCHGM"],"config_entries_subentries":{"01K9KQJJG0AXR3PCXD8Y3NCHGM":[null]},"configuration_url":null,"connections":[],"created_at":"2025-09-14T05:50:32.116778+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"10ca6ddb3fd866229110338ebd81d989","identifiers":[["litterrobot","PET-193d8c25-73bb-4843-8c37-a4e5df8987b5"]],"labels":[],"manufacturer":"Whisker","model":"Domestic_shorthair cat","model_id":null,"modified_at":"2025-11-09T07:27:10.929101+00:00","name_by_user":null,"name":"Arturo","primary_config_entry":"01K9KQJJG0AXR3PCXD8Y3NCHGM","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01K9KQJJG0AXR3PCXD8Y3NCHGM"],"config_entries_subentries":{"01K9KQJJG0AXR3PCXD8Y3NCHGM":[null]},"configuration_url":null,"connections":[],"created_at":"2025-09-14T05:50:32.117890+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"74e3b79ec6979e84665f1a770cf8ba0d","identifiers":[["litterrobot","PET-aa03c134-af7c-487b-951f-13756112a701"]],"labels":[],"manufacturer":"Whisker","model":"American_shorthair cat","model_id":null,"modified_at":"2025-11-09T07:27:10.929643+00:00","name_by_user":null,"name":"Emrys","primary_config_entry":"01K9KQJJG0AXR3PCXD8Y3NCHGM","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/989077527","connections":[],"created_at":"2025-11-30T03:49:47.588228+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"2732411e069c551daf1de7cb06910946","identifiers":[["hacs","989077527"]],"labels":[],"manufacturer":"cavefire","model":"integration","model_id":null,"modified_at":"2025-11-30T03:49:47.588440+00:00","name_by_user":null,"name":"openid","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/a0d7b954_tailscale","connections":[],"created_at":"2025-11-30T05:27:48.474089+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"924718a806a34c291e08665b03918a9e","identifiers":[["hassio","a0d7b954_tailscale"]],"labels":[],"manufacturer":"Home Assistant Community Add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2025-11-30T05:27:48.474110+00:00","name_by_user":null,"name":"Tailscale","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"0.26.1","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","ff:ff:b4:0e:06:02:fc:22"]],"created_at":"2025-12-08T20:52:39.754157+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"3e9f76ced7f9bfd888f501c832f56d79","identifiers":[["zha","ff:ff:b4:0e:06:02:fc:22"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RTHS24BZ","model_id":null,"modified_at":"2025-12-08T20:53:26.784163+00:00","name_by_user":"Humidity sensor 1","name":"Third Reality, Inc 3RTHS24BZ","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000025","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","ff:ff:b4:0e:06:03:14:0d"]],"created_at":"2025-12-09T15:09:51.777015+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"1798eca71ca7e6a2057f26460893dc1f","identifiers":[["zha","ff:ff:b4:0e:06:03:14:0d"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RTHS24BZ","model_id":null,"modified_at":"2025-12-09T15:10:06.650788+00:00","name_by_user":"Humidity 2","name":"Third Reality, Inc 3RTHS24BZ","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x00000025","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"bedroom","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","ff:ff:b4:0e:06:06:41:ca"]],"created_at":"2025-12-11T18:48:35.033465+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"899ae685d236a458fa035a61d30367fa","identifiers":[["zha","ff:ff:b4:0e:06:06:41:ca"]],"labels":["monitored"],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-12-31T06:32:00.682651+00:00","name_by_user":"Master bath fan","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/307098646","connections":[],"created_at":"2025-12-13T06:36:48.563621+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"376045287ad5290ff6cc0356ea3b1ce8","identifiers":[["hacs","307098646"]],"labels":[],"manufacturer":"nielsfaber","model":"integration","model_id":null,"modified_at":"2025-12-13T06:36:48.563664+00:00","name_by_user":null,"name":"Alarmo","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"misc","config_entries":["01KCB6VYHK4W6DCTV260B9RD3M"],"config_entries_subentries":{"01KCB6VYHK4W6DCTV260B9RD3M":[null]},"configuration_url":null,"connections":[],"created_at":"2025-12-13T06:37:44.884851+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"acf965d02296e7bf52a3c05c0cee8bad","identifiers":[["alarmo","10d15907fccf"]],"labels":[],"manufacturer":"@nielsfaber","model":"Alarmo","model_id":null,"modified_at":"2025-12-13T06:37:53.100453+00:00","name_by_user":null,"name":"Alarmo","primary_config_entry":"01KCB6VYHK4W6DCTV260B9RD3M","serial_number":null,"sw_version":"1.10.13","via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/939311749","connections":[],"created_at":"2025-12-15T03:24:11.076092+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"269d4c1881008301bd823d54665682af","identifiers":[["hacs","939311749"]],"labels":[],"manufacturer":"alexpfau","model":"plugin","model_id":null,"modified_at":"2025-12-15T03:24:11.076645+00:00","name_by_user":null,"name":"Calendar Card Pro","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":"living_room","config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"configuration_url":null,"connections":[["zigbee","ff:ff:b4:0e:06:06:47:70"]],"created_at":"2025-12-15T03:35:30.737897+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"411a10657bf317baba94da81ed456d3e","identifiers":[["zha","ff:ff:b4:0e:06:06:47:70"]],"labels":[],"manufacturer":"Third Reality, Inc","model":"3RSS009Z","model_id":null,"modified_at":"2025-12-15T03:44:43.241637+00:00","name_by_user":"unused switch","name":"Third Reality, Inc 3RSS009Z","primary_config_entry":"01JE2R7DPDQ8PCN6MMVWNFMDFN","serial_number":null,"sw_version":"0x0000001e","via_device_id":"a6b0311faa8ba235909a9316b2313cc8"},
|
||||||
|
{"area_id":"living_room","config_entries":["01KDH8M5SF0CTB4F4G6TXAP6SZ"],"config_entries_subentries":{"01KDH8M5SF0CTB4F4G6TXAP6SZ":[null]},"configuration_url":"https://192.168.0.210:443","connections":[["mac","8c:7a:b3:30:bb:52"]],"created_at":"2025-12-28T01:19:39.350151+00:00","disabled_by":null,"entry_type":null,"hw_version":"IPC_NT1NA45MP","id":"50c1be5a8001d3b2c9341a0a9013b5d4","identifiers":[["onvif","8c:7a:b3:30:bb:52"],["reolink","9527000K323YAM2G"]],"labels":[],"manufacturer":"Reolink","model":"E1 Pro","model_id":"E Series E330","modified_at":"2025-12-31T06:40:05.071501+00:00","name_by_user":null,"name":"cat cam 1","primary_config_entry":"01KDH8M5SF0CTB4F4G6TXAP6SZ","serial_number":"9527000K323YAM2G","sw_version":"v3.1.0.4417_2412122130","via_device_id":null},
|
||||||
|
{"area_id":"living_room","config_entries":["01KDH98BB0VPBMK5MYNVK08ZZE"],"config_entries_subentries":{"01KDH98BB0VPBMK5MYNVK08ZZE":[null]},"configuration_url":null,"connections":[],"created_at":"2025-12-28T01:30:36.769781+00:00","disabled_by":null,"entry_type":null,"hw_version":null,"id":"3d32fca1951073312072807194d69498","identifiers":[["generic","01KDH98BB0VPBMK5MYNVK08ZZE"]],"labels":[],"manufacturer":"Generic","model":null,"model_id":null,"modified_at":"2025-12-28T01:30:45.928530+00:00","name_by_user":"Cat Cam Feed","name":"192_168_0_210","primary_config_entry":"01KDH98BB0VPBMK5MYNVK08ZZE","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2SQ4DY1D75FFMD9PGMNER0"],"config_entries_subentries":{"01JE2SQ4DY1D75FFMD9PGMNER0":[null]},"configuration_url":"homeassistant://hacs/repository/394082552","connections":[],"created_at":"2025-12-28T01:58:12.841284+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"4352fe50de17b82f448500509c8403f9","identifiers":[["hacs","394082552"]],"labels":[],"manufacturer":"dermotduffy","model":"plugin","model_id":null,"modified_at":"2025-12-28T01:58:12.841304+00:00","name_by_user":null,"name":"Advanced Camera Card","primary_config_entry":"01JE2SQ4DY1D75FFMD9PGMNER0","serial_number":null,"sw_version":null,"via_device_id":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"configuration_url":"homeassistant://hassio/addon/core_samba","connections":[],"created_at":"2026-01-31T04:56:02.709713+00:00","disabled_by":null,"entry_type":"service","hw_version":null,"id":"f2c219cf3a169075d30362fc47cd2092","identifiers":[["hassio","core_samba"]],"labels":[],"manufacturer":"Official add-ons","model":"Home Assistant Add-on","model_id":null,"modified_at":"2026-01-31T04:56:02.709737+00:00","name_by_user":null,"name":"Samba share","primary_config_entry":"01JE2PA1AVW34BP3R20RJ09CVZ","serial_number":null,"sw_version":"12.5.4","via_device_id":null}
|
||||||
|
],
|
||||||
|
"deleted_devices": [
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"connections":[],"created_at":"2024-12-02T04:16:23.167541+00:00","disabled_by":null,"disabled_by_undefined":true,"identifiers":[["hassio","cb646a50_get"]],"id":"8c86e72f9b4b1e723c1e11420d8d8246","labels":[],"modified_at":"2024-12-02T04:18:04.003423+00:00","name_by_user":null,"orphaned_timestamp":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"connections":[],"created_at":"2025-11-04T02:53:43.127285+00:00","disabled_by":null,"disabled_by_undefined":false,"identifiers":[["hassio","2a018ee8_squeezelite1"]],"id":"7447759e3f364f084f58ef4fef289fd3","labels":[],"modified_at":"2025-11-04T02:58:42.750902+00:00","name_by_user":null,"orphaned_timestamp":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2R7DPDQ8PCN6MMVWNFMDFN"],"config_entries_subentries":{"01JE2R7DPDQ8PCN6MMVWNFMDFN":[null]},"connections":[["zigbee","00:15:8d:00:0a:f3:89:48"]],"created_at":"2024-12-04T03:30:01.766338+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["zha","00:15:8d:00:0a:f3:89:48"]],"id":"ca93d3e4bd1c8311594c93931ce0b269","labels":[],"modified_at":"2025-11-23T17:30:35.199602+00:00","name_by_user":"pending return","orphaned_timestamp":null},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"connections":[],"created_at":"2025-12-17T05:04:26.573243+00:00","disabled_by":null,"disabled_by_undefined":false,"identifiers":[["hassio","a0d7b954_nginxproxymanager"]],"id":"ebadfc9caddd8f8694ef8eae10d9d6cd","labels":[],"modified_at":"2025-12-17T05:13:15.092364+00:00","name_by_user":null,"orphaned_timestamp":null},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.783716+00:00","disabled_by":null,"disabled_by_undefined":false,"identifiers":[["tuya","tys3F3Oj0ssOzFCHtUe"]],"id":"72892ed9db345cf7128591bdd32679fd","labels":[],"modified_at":"2026-01-30T03:35:33.924182+00:00","name_by_user":null,"orphaned_timestamp":1769744133.9055798},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.638706+00:00","disabled_by":null,"disabled_by_undefined":false,"identifiers":[["tuya","eb951df86c9d584b4dxweb"]],"id":"ae0f584b8a4c29d9560123f19f4b4cae","labels":[],"modified_at":"2026-01-30T03:35:37.586118+00:00","name_by_user":null,"orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.639248+00:00","disabled_by":null,"disabled_by_undefined":false,"identifiers":[["tuya","eb266309af73dd8168qvda"]],"id":"db2d7eba70cfbb9407f6daf550c633f9","labels":[],"modified_at":"2026-01-30T03:35:37.588317+00:00","name_by_user":null,"orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.639728+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","eb2e83f79423674858pd1v"]],"id":"58b0b27e9307aa59cc938fed19598d6b","labels":[],"modified_at":"2026-01-30T03:35:37.590040+00:00","name_by_user":"unused7","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.639885+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","eb9c02171d09cebfc2mldh"]],"id":"b3b33083b1117f141e89a43787484471","labels":[],"modified_at":"2026-01-30T03:35:37.590483+00:00","name_by_user":"unused5","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.640035+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","ebd3b761cb158547e2plxf"]],"id":"6fd412561d4776f81b43b1a5e226ad89","labels":[],"modified_at":"2026-01-30T03:35:37.590922+00:00","name_by_user":"unused6","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.640205+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","eb5dfbb788a613dd337jxm"]],"id":"22451fef0266a6f4a4947785e17153f3","labels":[],"modified_at":"2026-01-30T03:35:37.591327+00:00","name_by_user":"unused8","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":"unused","config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.640434+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","eb5a3e353ef0ebed56dkgt"]],"id":"508a4085cc89b12429e4973467e58998","labels":[],"modified_at":"2026-01-30T03:35:37.591671+00:00","name_by_user":"unused","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.640580+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","eb4a288f8b6acfbb51wwba"]],"id":"e5a218d2a67e4b70c38af2c9b253714c","labels":[],"modified_at":"2026-01-30T03:35:37.592043+00:00","name_by_user":"unused4","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.640724+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","eb4761301bfb6e145876pn"]],"id":"5c7ee32fd4ec498948c09a7d53546625","labels":[],"modified_at":"2026-01-30T03:35:37.592392+00:00","name_by_user":"unused2","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.640870+00:00","disabled_by":"user","disabled_by_undefined":false,"identifiers":[["tuya","eb5267bd31fd931b26bcmk"]],"id":"8bbb95568635b8ae51254088009e9472","labels":[],"modified_at":"2026-01-30T03:35:37.592725+00:00","name_by_user":"unused3","orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":"garage","config_entries":[],"config_entries_subentries":{},"connections":[],"created_at":"2024-12-02T03:26:55.639552+00:00","disabled_by":null,"disabled_by_undefined":false,"identifiers":[["tuya","eb9771d7485c6103c1gohf"]],"id":"439c9948bce28a6cb03062294f5fa3f2","labels":[],"modified_at":"2026-01-30T03:35:37.593103+00:00","name_by_user":null,"orphaned_timestamp":1769744137.5860384},
|
||||||
|
{"area_id":null,"config_entries":["01JE2PA1AVW34BP3R20RJ09CVZ"],"config_entries_subentries":{"01JE2PA1AVW34BP3R20RJ09CVZ":[null]},"connections":[],"created_at":"2025-12-31T05:56:36.749132+00:00","disabled_by":null,"disabled_by_undefined":false,"identifiers":[["hassio","a0d7b954_vscode"]],"id":"f958e992824582d715ca5082a6bcf892","labels":[],"modified_at":"2026-01-31T04:56:02.634130+00:00","name_by_user":null,"orphaned_timestamp":null}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
1040
.storage/core.entity_registry
Normal file
1040
.storage/core.entity_registry
Normal file
File diff suppressed because it is too large
Load Diff
72
.storage/core.label_registry
Normal file
72
.storage/core.label_registry
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 2,
|
||||||
|
"key": "core.label_registry",
|
||||||
|
"data": {
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"color": null,
|
||||||
|
"description": null,
|
||||||
|
"icon": "mdi:fan",
|
||||||
|
"label_id": "bedroom_fan",
|
||||||
|
"name": "Bedroom Fan",
|
||||||
|
"created_at": "2024-12-23T23:55:42.769576+00:00",
|
||||||
|
"modified_at": "2024-12-23T23:55:42.769578+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": null,
|
||||||
|
"description": null,
|
||||||
|
"icon": null,
|
||||||
|
"label_id": "kitchen_sink",
|
||||||
|
"name": "kitchen sink",
|
||||||
|
"created_at": "2025-02-15T20:30:44.679131+00:00",
|
||||||
|
"modified_at": "2025-02-15T20:30:44.679141+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": null,
|
||||||
|
"description": null,
|
||||||
|
"icon": null,
|
||||||
|
"label_id": "downstair_bathroom_sink",
|
||||||
|
"name": "Downstair Bathroom Sink",
|
||||||
|
"created_at": "2025-02-15T22:13:40.836699+00:00",
|
||||||
|
"modified_at": "2025-02-15T22:13:40.836708+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": null,
|
||||||
|
"description": null,
|
||||||
|
"icon": null,
|
||||||
|
"label_id": "officesink",
|
||||||
|
"name": "OfficeSink",
|
||||||
|
"created_at": "2025-02-15T22:13:54.343477+00:00",
|
||||||
|
"modified_at": "2025-02-15T22:13:54.343484+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": null,
|
||||||
|
"description": null,
|
||||||
|
"icon": null,
|
||||||
|
"label_id": "bedroomsink",
|
||||||
|
"name": "BedroomSink",
|
||||||
|
"created_at": "2025-02-15T22:14:08.089260+00:00",
|
||||||
|
"modified_at": "2025-02-15T22:14:08.089262+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": null,
|
||||||
|
"description": null,
|
||||||
|
"icon": null,
|
||||||
|
"label_id": "waterheater",
|
||||||
|
"name": "WaterHeater",
|
||||||
|
"created_at": "2025-02-15T22:14:19.496443+00:00",
|
||||||
|
"modified_at": "2025-02-15T22:14:19.496451+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": null,
|
||||||
|
"description": null,
|
||||||
|
"icon": "mdi:alarm-light",
|
||||||
|
"label_id": "monitored",
|
||||||
|
"name": "Monitored",
|
||||||
|
"created_at": "2025-11-23T07:22:10.879929+00:00",
|
||||||
|
"modified_at": "2025-11-23T07:22:10.879937+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
8
.storage/core.logger
Normal file
8
.storage/core.logger
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "core.logger",
|
||||||
|
"data": {
|
||||||
|
"logs": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
2146
.storage/core.restore_state
Normal file
2146
.storage/core.restore_state
Normal file
File diff suppressed because it is too large
Load Diff
8
.storage/core.uuid
Normal file
8
.storage/core.uuid
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "core.uuid",
|
||||||
|
"data": {
|
||||||
|
"uuid": "3350d98d61e8432e830448e3f05fc88e"
|
||||||
|
}
|
||||||
|
}
|
||||||
185
.storage/esphome.01JR73AMVCG4FGGV23MYMXWNJT
Normal file
185
.storage/esphome.01JR73AMVCG4FGGV23MYMXWNJT
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "esphome.01JR73AMVCG4FGGV23MYMXWNJT",
|
||||||
|
"data": {
|
||||||
|
"device_info": {
|
||||||
|
"uses_password": false,
|
||||||
|
"name": "home-assistant-voice-0981b4",
|
||||||
|
"friendly_name": "Home Assistant Voice 0981b4",
|
||||||
|
"mac_address": "20:F8:3B:09:81:B4",
|
||||||
|
"compilation_time": "Jun 29 2025, 14:08:10",
|
||||||
|
"model": "esp32-s3-devkitc-1",
|
||||||
|
"manufacturer": "Espressif",
|
||||||
|
"has_deep_sleep": false,
|
||||||
|
"esphome_version": "2025.6.2",
|
||||||
|
"project_name": "Nabu Casa.Home Assistant Voice PE",
|
||||||
|
"project_version": "25.6.0",
|
||||||
|
"webserver_port": 0,
|
||||||
|
"legacy_voice_assistant_version": 1,
|
||||||
|
"voice_assistant_feature_flags": 61,
|
||||||
|
"legacy_bluetooth_proxy_version": 0,
|
||||||
|
"bluetooth_proxy_feature_flags": 0,
|
||||||
|
"zwave_proxy_feature_flags": 0,
|
||||||
|
"zwave_home_id": 0,
|
||||||
|
"suggested_area": "",
|
||||||
|
"bluetooth_mac_address": "",
|
||||||
|
"api_encryption_supported": false,
|
||||||
|
"devices": [],
|
||||||
|
"areas": [],
|
||||||
|
"area": {
|
||||||
|
"area_id": 0,
|
||||||
|
"name": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": [],
|
||||||
|
"api_version": {
|
||||||
|
"major": 1,
|
||||||
|
"minor": 10
|
||||||
|
},
|
||||||
|
"media_player": [
|
||||||
|
{
|
||||||
|
"object_id": "media_player",
|
||||||
|
"key": 2232357057,
|
||||||
|
"name": "Media Player",
|
||||||
|
"disabled_by_default": false,
|
||||||
|
"icon": "",
|
||||||
|
"entity_category": 0,
|
||||||
|
"device_id": 0,
|
||||||
|
"supports_pause": true,
|
||||||
|
"supported_formats": [
|
||||||
|
{
|
||||||
|
"format": "flac",
|
||||||
|
"sample_rate": 48000,
|
||||||
|
"num_channels": 1,
|
||||||
|
"purpose": 1,
|
||||||
|
"sample_bytes": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "flac",
|
||||||
|
"sample_rate": 48000,
|
||||||
|
"num_channels": 2,
|
||||||
|
"purpose": 0,
|
||||||
|
"sample_bytes": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"feature_flags": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"binary_sensor": [],
|
||||||
|
"light": [
|
||||||
|
{
|
||||||
|
"object_id": "led_ring",
|
||||||
|
"key": 1459351809,
|
||||||
|
"name": "LED Ring",
|
||||||
|
"disabled_by_default": false,
|
||||||
|
"icon": "mdi:circle-outline",
|
||||||
|
"entity_category": 1,
|
||||||
|
"device_id": 0,
|
||||||
|
"supported_color_modes": [
|
||||||
|
35
|
||||||
|
],
|
||||||
|
"min_mireds": 0.0,
|
||||||
|
"max_mireds": 0.0,
|
||||||
|
"effects": [],
|
||||||
|
"legacy_supports_brightness": true,
|
||||||
|
"legacy_supports_rgb": true,
|
||||||
|
"legacy_supports_white_value": false,
|
||||||
|
"legacy_supports_color_temperature": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"select": [
|
||||||
|
{
|
||||||
|
"object_id": "wake_word_sensitivity",
|
||||||
|
"key": 666792156,
|
||||||
|
"name": "Wake word sensitivity",
|
||||||
|
"disabled_by_default": false,
|
||||||
|
"icon": "",
|
||||||
|
"entity_category": 1,
|
||||||
|
"device_id": 0,
|
||||||
|
"options": [
|
||||||
|
"Slightly sensitive",
|
||||||
|
"Moderately sensitive",
|
||||||
|
"Very sensitive"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"button": [
|
||||||
|
{
|
||||||
|
"object_id": "restart",
|
||||||
|
"key": 1203400786,
|
||||||
|
"name": "Restart",
|
||||||
|
"disabled_by_default": true,
|
||||||
|
"icon": "mdi:restart",
|
||||||
|
"entity_category": 1,
|
||||||
|
"device_id": 0,
|
||||||
|
"device_class": "restart"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"update": [
|
||||||
|
{
|
||||||
|
"object_id": "home_assistant_voice_0981b4",
|
||||||
|
"key": 1265841631,
|
||||||
|
"name": "",
|
||||||
|
"disabled_by_default": false,
|
||||||
|
"icon": "",
|
||||||
|
"entity_category": 1,
|
||||||
|
"device_id": 0,
|
||||||
|
"device_class": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"event": [
|
||||||
|
{
|
||||||
|
"object_id": "button_press",
|
||||||
|
"key": 2698747613,
|
||||||
|
"name": "Button press",
|
||||||
|
"disabled_by_default": false,
|
||||||
|
"icon": "mdi:button-pointer",
|
||||||
|
"entity_category": 0,
|
||||||
|
"device_id": 0,
|
||||||
|
"device_class": "button",
|
||||||
|
"event_types": [
|
||||||
|
"double_press",
|
||||||
|
"easter_egg_press",
|
||||||
|
"long_press",
|
||||||
|
"triple_press"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"switch": [
|
||||||
|
{
|
||||||
|
"object_id": "mute",
|
||||||
|
"key": 2974103762,
|
||||||
|
"name": "Mute",
|
||||||
|
"disabled_by_default": false,
|
||||||
|
"icon": "mdi:microphone-off",
|
||||||
|
"entity_category": 1,
|
||||||
|
"device_id": 0,
|
||||||
|
"assumed_state": false,
|
||||||
|
"device_class": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"object_id": "wake_sound",
|
||||||
|
"key": 3517166597,
|
||||||
|
"name": "Wake sound",
|
||||||
|
"disabled_by_default": false,
|
||||||
|
"icon": "mdi:bullhorn",
|
||||||
|
"entity_category": 1,
|
||||||
|
"device_id": 0,
|
||||||
|
"assumed_state": false,
|
||||||
|
"device_class": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"object_id": "beta_firmware",
|
||||||
|
"key": 3963827797,
|
||||||
|
"name": "Beta firmware",
|
||||||
|
"disabled_by_default": true,
|
||||||
|
"icon": "mdi:test-tube",
|
||||||
|
"entity_category": 1,
|
||||||
|
"device_id": 0,
|
||||||
|
"assumed_state": false,
|
||||||
|
"device_class": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
10
.storage/frontend.system_data
Normal file
10
.storage/frontend.system_data
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "frontend.system_data",
|
||||||
|
"data": {
|
||||||
|
"core": {
|
||||||
|
"default_panel": "my-dashboard-1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
.storage/frontend.user_data_4a285b563f6a4a54b657acf1926c0b5b
Normal file
18
.storage/frontend.user_data_4a285b563f6a4a54b657acf1926c0b5b
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "frontend.user_data_4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"data": {
|
||||||
|
"language": {
|
||||||
|
"language": "en",
|
||||||
|
"number_format": "language",
|
||||||
|
"time_format": "language",
|
||||||
|
"date_format": "language",
|
||||||
|
"time_zone": "local",
|
||||||
|
"first_weekday": "language"
|
||||||
|
},
|
||||||
|
"core": {
|
||||||
|
"showAdvanced": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
.storage/frontend.user_data_9ba78f5ab4e646758f3c093bd79ac9c8
Normal file
15
.storage/frontend.user_data_9ba78f5ab4e646758f3c093bd79ac9c8
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "frontend.user_data_9ba78f5ab4e646758f3c093bd79ac9c8",
|
||||||
|
"data": {
|
||||||
|
"language": {
|
||||||
|
"language": "en",
|
||||||
|
"number_format": "language",
|
||||||
|
"time_format": "language",
|
||||||
|
"date_format": "language",
|
||||||
|
"time_zone": "local",
|
||||||
|
"first_weekday": "language"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6628
.storage/google.01JJGQCFA4BSXH2EK2EN1ZD3AA
Normal file
6628
.storage/google.01JJGQCFA4BSXH2EK2EN1ZD3AA
Normal file
File diff suppressed because it is too large
Load Diff
6213
.storage/google.01K70AXB81Z95CMX993D4Z9K08
Normal file
6213
.storage/google.01K70AXB81Z95CMX993D4Z9K08
Normal file
File diff suppressed because it is too large
Load Diff
13
.storage/hacs.critical
Normal file
13
.storage/hacs.critical
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "hacs.critical",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"repository": "test/test",
|
||||||
|
"reason": "Security issues, known to steal auth tokens.",
|
||||||
|
"link": "https://github.com/hacs/default/pull/2",
|
||||||
|
"acknowledged": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
12732
.storage/hacs.data
Normal file
12732
.storage/hacs.data
Normal file
File diff suppressed because it is too large
Load Diff
10
.storage/hacs.hacs
Normal file
10
.storage/hacs.hacs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "6",
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "hacs.hacs",
|
||||||
|
"data": {
|
||||||
|
"archived_repositories": [],
|
||||||
|
"renamed_repositories": {},
|
||||||
|
"ignored_repositories": []
|
||||||
|
}
|
||||||
|
}
|
||||||
42025
.storage/hacs.repositories
Normal file
42025
.storage/hacs.repositories
Normal file
File diff suppressed because it is too large
Load Diff
8
.storage/hassio
Normal file
8
.storage/hassio
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "hassio",
|
||||||
|
"data": {
|
||||||
|
"hassio_user": "5e62410fa88e4aa7ab4ac7b9c4ba5dc1"
|
||||||
|
}
|
||||||
|
}
|
||||||
3083
.storage/homeassistant.exposed_entities
Normal file
3083
.storage/homeassistant.exposed_entities
Normal file
File diff suppressed because it is too large
Load Diff
19
.storage/http
Normal file
19
.storage/http
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "http",
|
||||||
|
"data": {
|
||||||
|
"use_x_forwarded_for": true,
|
||||||
|
"trusted_proxies": [
|
||||||
|
"192.168.0.249"
|
||||||
|
],
|
||||||
|
"ip_ban_enabled": true,
|
||||||
|
"cors_allowed_origins": [
|
||||||
|
"https://cast.home-assistant.io"
|
||||||
|
],
|
||||||
|
"server_port": 8123,
|
||||||
|
"use_x_frame_options": true,
|
||||||
|
"ssl_profile": "modern",
|
||||||
|
"login_attempts_threshold": -1
|
||||||
|
}
|
||||||
|
}
|
||||||
8
.storage/http.auth
Normal file
8
.storage/http.auth
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "http.auth",
|
||||||
|
"data": {
|
||||||
|
"content_user": "23e04b69df624a0fbff05ad27b5a1f0a"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
.storage/image
Normal file
16
.storage/image
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "image",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "45f6eb5e47c9a4396f538f6440ddadee",
|
||||||
|
"filesize": 36430,
|
||||||
|
"content_type": "image/png",
|
||||||
|
"name": "fpfin.png",
|
||||||
|
"uploaded_at": "2026-01-25T18:59:19.511080+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
8
.storage/input_boolean
Normal file
8
.storage/input_boolean
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "input_boolean",
|
||||||
|
"data": {
|
||||||
|
"items": []
|
||||||
|
}
|
||||||
|
}
|
||||||
29
.storage/input_button
Normal file
29
.storage/input_button
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "input_button",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "piholepause5",
|
||||||
|
"name": "PiHolePause5",
|
||||||
|
"icon": "mdi:advertisements"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pauseads",
|
||||||
|
"name": "Pauseads",
|
||||||
|
"icon": "mdi:advertisements"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fix_stair_light",
|
||||||
|
"name": "fix_stair_light",
|
||||||
|
"icon": "mdi:glass-fragile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dummybuttonfornh",
|
||||||
|
"name": "DummyButtonForNH",
|
||||||
|
"icon": "mdi:bug-stop"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
.storage/lovelace.dashboard_darkboard
Normal file
20
.storage/lovelace.dashboard_darkboard
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "lovelace.dashboard_darkboard",
|
||||||
|
"data": {
|
||||||
|
"config": {
|
||||||
|
"kiosk_mode": {
|
||||||
|
"hide_header": true
|
||||||
|
},
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"title": "Home",
|
||||||
|
"type": "sections",
|
||||||
|
"cards": [],
|
||||||
|
"sections": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
.storage/lovelace.map
Normal file
12
.storage/lovelace.map
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "lovelace.map",
|
||||||
|
"data": {
|
||||||
|
"config": {
|
||||||
|
"strategy": {
|
||||||
|
"type": "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
557
.storage/lovelace.my_dashboard_1
Normal file
557
.storage/lovelace.my_dashboard_1
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "lovelace.my_dashboard_1",
|
||||||
|
"data": {
|
||||||
|
"config": {
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"title": "Home",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading_style": "title",
|
||||||
|
"icon": "mdi:map-marker",
|
||||||
|
"heading": "Rooms"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Bedroom",
|
||||||
|
"icon": "mdi:bed",
|
||||||
|
"sub_button": [
|
||||||
|
{
|
||||||
|
"entity": "light.lamp_lights",
|
||||||
|
"icon": "mdi:desk-lamp",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "switch.bedside_fan_switch",
|
||||||
|
"icon": "mdi:fan",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "switch.bedroom_ceiling_fan_switch",
|
||||||
|
"icon": "mdi:ceiling-fan",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "switch.bedroom_ceiling_lights_switch_3",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "fan.bedroom_air_purifier",
|
||||||
|
"icon": "mdi:air-filter",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Living room",
|
||||||
|
"icon": "",
|
||||||
|
"sub_button": [
|
||||||
|
{
|
||||||
|
"entity": "switch.stair_light_lower_switch",
|
||||||
|
"icon": "mdi:stairs",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "switch.upper_landing_a_switch",
|
||||||
|
"icon": "mdi:door-sliding-open",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "light.night_light_1_light",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "light.bookcase",
|
||||||
|
"icon": "mdi:bookshelf",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "light.figurecase_light",
|
||||||
|
"icon": "mdi:book-open-variant-outline",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "light.movie_case_right_light",
|
||||||
|
"icon": "mdi:movie-open",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Bathroom",
|
||||||
|
"icon": "mdi:bathtub",
|
||||||
|
"sub_button": [
|
||||||
|
{
|
||||||
|
"entity": "light.master_bathroom_lights",
|
||||||
|
"icon": "mdi:string-lights",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Office",
|
||||||
|
"icon": "mdi:desk",
|
||||||
|
"sub_button": [
|
||||||
|
{
|
||||||
|
"entity": "switch.office_ceiling_fan_switch_2",
|
||||||
|
"icon": "mdi:ceiling-fan",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "switch.office_fan_plug_switch_2",
|
||||||
|
"icon": "mdi:fan",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Garage",
|
||||||
|
"icon": "mdi:garage",
|
||||||
|
"sub_button": [
|
||||||
|
{
|
||||||
|
"entity": "switch.3d_printer_light_switch",
|
||||||
|
"name": "3d printer light",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "Misc",
|
||||||
|
"heading_style": "title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "weather-forecast",
|
||||||
|
"entity": "weather.forecast_home",
|
||||||
|
"forecast_type": "daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "gauge",
|
||||||
|
"entity": "sensor.emrys_throne_waste_drawer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entities": [
|
||||||
|
"calendar.critical_events",
|
||||||
|
{
|
||||||
|
"entity": "calendar.home_tasks_2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "calendar.holidays_in_united_states_2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"days_to_show": 30,
|
||||||
|
"weather": {
|
||||||
|
"position": "date",
|
||||||
|
"date": {
|
||||||
|
"show_conditions": true,
|
||||||
|
"show_high_temp": true,
|
||||||
|
"show_low_temp": false,
|
||||||
|
"icon_size": "14px",
|
||||||
|
"font_size": "12px",
|
||||||
|
"color": "var(--primary-text-color)"
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"show_conditions": true,
|
||||||
|
"show_temp": true,
|
||||||
|
"icon_size": "14px",
|
||||||
|
"font_size": "12px",
|
||||||
|
"color": "var(--primary-text-color)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "custom:calendar-card-pro"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 5,
|
||||||
|
"dense_section_placement": true,
|
||||||
|
"cards": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"title": "Batteries",
|
||||||
|
"path": "batteries",
|
||||||
|
"icon": "mdi:clock-digital",
|
||||||
|
"dense_section_placement": true,
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "timer.night_light_1_off_timer",
|
||||||
|
"grid_options": {
|
||||||
|
"columns": 12,
|
||||||
|
"rows": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cards": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"icon": "mdi:alarm-light",
|
||||||
|
"path": "",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"states": [
|
||||||
|
"arm_home",
|
||||||
|
"arm_away",
|
||||||
|
"arm_vacation"
|
||||||
|
],
|
||||||
|
"type": "alarm-panel",
|
||||||
|
"entity": "alarm_control_panel.alarmo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"title": "Cats",
|
||||||
|
"path": "cats",
|
||||||
|
"icon": "mdi:cat",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "statistic",
|
||||||
|
"entity": "sensor.emrys_weight",
|
||||||
|
"period": {
|
||||||
|
"calendar": {
|
||||||
|
"period": "month"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stat_type": "mean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "statistic",
|
||||||
|
"entity": "sensor.arturo_weight",
|
||||||
|
"period": {
|
||||||
|
"calendar": {
|
||||||
|
"period": "month"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stat_type": "mean"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chart_type": "line",
|
||||||
|
"period": "week",
|
||||||
|
"type": "statistics-graph",
|
||||||
|
"entities": [
|
||||||
|
"sensor.emrys_weight",
|
||||||
|
"sensor.arturo_weight"
|
||||||
|
],
|
||||||
|
"stat_types": [
|
||||||
|
"mean"
|
||||||
|
],
|
||||||
|
"title": "Cats weight",
|
||||||
|
"days_to_show": 120
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:advanced-camera-card",
|
||||||
|
"cameras": [
|
||||||
|
{
|
||||||
|
"camera_entity": "camera.cat_cam_1_fluent",
|
||||||
|
"title": "Cat Camera"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"live": {
|
||||||
|
"preload": true,
|
||||||
|
"lazy_unload": [
|
||||||
|
"hidden"
|
||||||
|
],
|
||||||
|
"auto_play": [
|
||||||
|
"visible"
|
||||||
|
],
|
||||||
|
"auto_pause": [
|
||||||
|
"hidden"
|
||||||
|
],
|
||||||
|
"show_image_during_load": true
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"buttons": {
|
||||||
|
"camera_ui": {
|
||||||
|
"icon": "mdi:account",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"ptz_controls": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "switch.cat_cam_1_privacy_mode"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"icon": "mdi:lan-disconnect",
|
||||||
|
"path": "",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "markdown",
|
||||||
|
"content": "http://192.168.0.249:20720/admin/api.php?disable=300&auth=c874330af40074c580efdb36d0476e216d3bc3ffb860dcfd9361da5a457770f5",
|
||||||
|
"title": "Pause adblock"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"icon": "mdi:battery-20",
|
||||||
|
"path": "",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.bedroom_button_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.bedroom_ceiling_fan_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.bedroom_ceiling_lights_battery_3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.gabys_phone_battery_level"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.masterbathbutton2_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.office_ceiling_fan_battery_2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.stair_light_lower_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.stair_light_upper_battery_2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.bedroom_button_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.bedroom_ceiling_fan_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.bedroom_ceiling_lights_battery_3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.leak_sensor_1_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.leak_sensor_2_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.leak_sensor_3_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.leak_sensor_4_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.leak_sensor_5_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.master_bath_outer_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.masterbathbutton2_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.office_ceiling_fan_battery_2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.room_exit_button_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.stair_light_lower_battery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.stair_light_upper_battery_2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tile",
|
||||||
|
"entity": "sensor.upper_landing_b_battery"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
647
.storage/lovelace.new_home
Normal file
647
.storage/lovelace.new_home
Normal file
@@ -0,0 +1,647 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "lovelace.new_home",
|
||||||
|
"data": {
|
||||||
|
"config": {
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"title": "Home",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "Rooms"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Kitchen",
|
||||||
|
"icon": "mdi:fridge",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "none"
|
||||||
|
},
|
||||||
|
"button_action": {
|
||||||
|
"tap_action": {
|
||||||
|
"navigation_path": "kitchen"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "kitchen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "kitchen"
|
||||||
|
},
|
||||||
|
"sub_button": [
|
||||||
|
{
|
||||||
|
"entity": "light.kitchen_lights_new_home"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Master Bedroom",
|
||||||
|
"icon": "mdi:chess-king",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "none"
|
||||||
|
},
|
||||||
|
"button_action": {
|
||||||
|
"tap_action": {
|
||||||
|
"navigation_path": "master-bedroom"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "master-bedroom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "master-bedroom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Guest Bedroom",
|
||||||
|
"icon": "mdi:bed",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "none"
|
||||||
|
},
|
||||||
|
"button_action": {
|
||||||
|
"tap_action": {
|
||||||
|
"navigation_path": "guest-bedroom"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "guest-bedroom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "guest-bedroom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "custom:bubble-card",
|
||||||
|
"card_type": "button",
|
||||||
|
"button_type": "name",
|
||||||
|
"name": "Entry",
|
||||||
|
"icon": "mdi:door",
|
||||||
|
"tap_action": {
|
||||||
|
"action": "none"
|
||||||
|
},
|
||||||
|
"button_action": {
|
||||||
|
"tap_action": {
|
||||||
|
"navigation_path": "entry"
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "entry"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hold_action": {
|
||||||
|
"action": "navigate",
|
||||||
|
"navigation_path": "entry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"states": [
|
||||||
|
"arm_home",
|
||||||
|
"arm_away",
|
||||||
|
"arm_vacation"
|
||||||
|
],
|
||||||
|
"type": "alarm-panel",
|
||||||
|
"entity": "alarm_control_panel.alarmo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"title": "Kitchen",
|
||||||
|
"path": "kitchen",
|
||||||
|
"icon": "mdi:fridge",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "Lights",
|
||||||
|
"heading_style": "title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"show_name": true,
|
||||||
|
"show_icon": true,
|
||||||
|
"type": "button",
|
||||||
|
"entity": "light.kitchen_lights_new_home",
|
||||||
|
"hold_action": {
|
||||||
|
"action": "more-info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"header": {
|
||||||
|
"card": {
|
||||||
|
"type": "markdown",
|
||||||
|
"text_only": true,
|
||||||
|
"content": "# Kitchen\nTap to turn things on/off, hold to see more options ✨"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"title": "Master Bedroom",
|
||||||
|
"path": "master-bedroom",
|
||||||
|
"icon": "mdi:chess-king",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"title": "Guest Bedroom",
|
||||||
|
"path": "guest-bedroom",
|
||||||
|
"icon": "mdi:bed",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"title": "entry",
|
||||||
|
"path": "entry",
|
||||||
|
"icon": "mdi:door",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"heading": "New section"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sections",
|
||||||
|
"max_columns": 4,
|
||||||
|
"title": "Map",
|
||||||
|
"path": "map",
|
||||||
|
"icon": "mdi:map-marker-radius",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"type": "picture-elements",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_primary_bedroom_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "17%",
|
||||||
|
"left": "5%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_primary_bath_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "28%",
|
||||||
|
"left": "5%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_primary_closet_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "50%",
|
||||||
|
"left": "5%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_primary_toilet_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "42%",
|
||||||
|
"left": "13%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_entry_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "70%",
|
||||||
|
"left": "22%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_garage_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "70%",
|
||||||
|
"left": "10%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_garage_entry_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "62%",
|
||||||
|
"left": "17%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_laundry_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "51%",
|
||||||
|
"left": "12%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_storage_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "51%",
|
||||||
|
"left": "16%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_kitchen_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "52%",
|
||||||
|
"left": "30%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_pantry_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "60%",
|
||||||
|
"left": "33%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_powder_bath_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "60%",
|
||||||
|
"left": "28%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_office_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "82%",
|
||||||
|
"left": "30%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_living_room_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "25%",
|
||||||
|
"left": "25%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_patio_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "10%",
|
||||||
|
"left": "23%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_library_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "85%",
|
||||||
|
"left": "55%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_loft_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "45%",
|
||||||
|
"left": "70%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_guest_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "65%",
|
||||||
|
"left": "70%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_guest_bath_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "73%",
|
||||||
|
"left": "45%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_movie_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "60%",
|
||||||
|
"left": "45%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_upstairs_hall_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "65%",
|
||||||
|
"left": "58%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"entity": "light.nh_stair_lights",
|
||||||
|
"state_image": {
|
||||||
|
"on": "/local/images/light_on.svg",
|
||||||
|
"off": "/local/images/light_off.svg"
|
||||||
|
},
|
||||||
|
"tap_action": {
|
||||||
|
"action": "toggle"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"top": "45%",
|
||||||
|
"left": "56%",
|
||||||
|
"width": "2.4%",
|
||||||
|
"transform": "translate(-50%, -50%)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"image": {
|
||||||
|
"media_content_id": "media-source://image_upload/45f6eb5e47c9a4396f538f6440ddadee",
|
||||||
|
"media_content_type": "image/png",
|
||||||
|
"metadata": {
|
||||||
|
"title": "fpfin.png",
|
||||||
|
"thumbnail": "/api/image/serve/45f6eb5e47c9a4396f538f6440ddadee/256x256",
|
||||||
|
"media_class": "image",
|
||||||
|
"navigateIds": [
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
"media_content_type": "app",
|
||||||
|
"media_content_id": "media-source://image_upload"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"grid_options": {
|
||||||
|
"columns": "full",
|
||||||
|
"rows": "auto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"column_span": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
.storage/lovelace_dashboards
Normal file
43
.storage/lovelace_dashboards
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "lovelace_dashboards",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "map",
|
||||||
|
"icon": "mdi:map",
|
||||||
|
"title": "Map",
|
||||||
|
"url_path": "map",
|
||||||
|
"show_in_sidebar": true,
|
||||||
|
"mode": "storage",
|
||||||
|
"require_admin": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "my_dashboard_1",
|
||||||
|
"show_in_sidebar": true,
|
||||||
|
"title": "My dashboard 1",
|
||||||
|
"require_admin": false,
|
||||||
|
"mode": "storage",
|
||||||
|
"url_path": "my-dashboard-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dashboard_darkboard",
|
||||||
|
"show_in_sidebar": false,
|
||||||
|
"title": "Darkboard",
|
||||||
|
"require_admin": false,
|
||||||
|
"mode": "storage",
|
||||||
|
"url_path": "dashboard-darkboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "new_home",
|
||||||
|
"show_in_sidebar": true,
|
||||||
|
"icon": "mdi:home",
|
||||||
|
"title": "New Home",
|
||||||
|
"require_admin": false,
|
||||||
|
"mode": "storage",
|
||||||
|
"url_path": "new-home"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
34
.storage/lovelace_resources
Normal file
34
.storage/lovelace_resources
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "lovelace_resources",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "8fc8acc609894063aac434ef37af1d36",
|
||||||
|
"url": "/hacsfiles/Bubble-Card/bubble-card.js?hacstag=680112919230",
|
||||||
|
"type": "module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "764f87cd307b41ffb4491ea743427729",
|
||||||
|
"url": "/hacsfiles/kiosk-mode/kiosk-mode.js?hacstag=497319128902",
|
||||||
|
"type": "module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "91712a5ce5e94b43a7141f83d7750e52",
|
||||||
|
"url": "/hacsfiles/custom-sidebar/custom-sidebar.js?hacstag=7377802181110",
|
||||||
|
"type": "module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c83e141a2c2a4ec0bf2446a612e2b427",
|
||||||
|
"url": "/hacsfiles/calendar-card-pro/calendar-card-pro.js?hacstag=939311749310",
|
||||||
|
"type": "module"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4208c7441471440c84b3ea8dead19e92",
|
||||||
|
"url": "/hacsfiles/advanced-camera-card/advanced-camera-card.js?hacstag=3940825527260",
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
10
.storage/mobile_app
Normal file
10
.storage/mobile_app
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "mobile_app",
|
||||||
|
"data": {
|
||||||
|
"deleted_ids": [
|
||||||
|
"837c37d8eeeab61e356ae0584dae4e0912b24629a2430b8eaa01fb031b1b8a2e"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
13
.storage/onboarding
Normal file
13
.storage/onboarding
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "onboarding",
|
||||||
|
"data": {
|
||||||
|
"done": [
|
||||||
|
"user",
|
||||||
|
"core_config",
|
||||||
|
"analytics",
|
||||||
|
"integration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
18
.storage/person
Normal file
18
.storage/person
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "person",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "christopher_chasteen",
|
||||||
|
"name": "Christopher Chasteen",
|
||||||
|
"user_id": "4a285b563f6a4a54b657acf1926c0b5b",
|
||||||
|
"device_trackers": [
|
||||||
|
"device_tracker.chris_phone",
|
||||||
|
"device_tracker.pixel_3a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
6
.storage/reolink.01KDH8M5SF0CTB4F4G6TXAP6SZ.json
Normal file
6
.storage/reolink.01KDH8M5SF0CTB4F4G6TXAP6SZ.json
Normal file
File diff suppressed because one or more lines are too long
105
.storage/repairs.issue_registry
Normal file
105
.storage/repairs.issue_registry
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 2,
|
||||||
|
"key": "repairs.issue_registry",
|
||||||
|
"data": {
|
||||||
|
"issues": [
|
||||||
|
{
|
||||||
|
"created": "2025-01-18T02:32:22.388559+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "hacs",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "restart_required_172733314_tags/2.0.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2025-02-05T02:15:26.564647+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "hacs",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "restart_required_172733314_tags/2.0.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2025-09-14T05:37:02.203310+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "hacs",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "restart_required_249381778_tags/v5.2.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2025-12-13T06:36:48.560837+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "hacs",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "restart_required_307098646_tags/v1.10.13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2025-12-20T01:09:38.496893+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "cloud",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "connection_error_2026-12-17 00:00:00+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2025-12-20T01:09:40.871353+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "homeassistant",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "config_entry_reauth_vesync_01JFP966AYW48GQ7FPY42M80G0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2025-12-20T01:11:15.598983+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "hassio",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "3e4e8553cc4947e18bc54ea66be0de5b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2025-12-20T01:51:59.482337+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "hassio",
|
||||||
|
"is_persistent": false,
|
||||||
|
"issue_id": "c94402f948f641358bf982cdd8eddff8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2026-01-03T16:00:00.131529+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "automation",
|
||||||
|
"is_persistent": true,
|
||||||
|
"issue_id": "automation.battery_check_service_not_found_notify.mobile_app_cph2513",
|
||||||
|
"breaks_in_ha_version": null,
|
||||||
|
"data": null,
|
||||||
|
"is_fixable": true,
|
||||||
|
"issue_domain": null,
|
||||||
|
"learn_more_url": null,
|
||||||
|
"severity": "error",
|
||||||
|
"translation_key": "service_not_found",
|
||||||
|
"translation_placeholders": {
|
||||||
|
"service": "notify.mobile_app_cph2513",
|
||||||
|
"entity_id": "automation.battery_check",
|
||||||
|
"name": "Battery Check",
|
||||||
|
"edit": "/config/automation/edit/1736309562377"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"created": "2026-01-04T11:52:28.396717+00:00",
|
||||||
|
"dismissed_version": null,
|
||||||
|
"domain": "automation",
|
||||||
|
"is_persistent": true,
|
||||||
|
"issue_id": "automation.litter_box_full_service_not_found_notify.mobile_app_cph2513",
|
||||||
|
"breaks_in_ha_version": null,
|
||||||
|
"data": null,
|
||||||
|
"is_fixable": true,
|
||||||
|
"issue_domain": null,
|
||||||
|
"learn_more_url": null,
|
||||||
|
"severity": "error",
|
||||||
|
"translation_key": "service_not_found",
|
||||||
|
"translation_placeholders": {
|
||||||
|
"service": "notify.mobile_app_cph2513",
|
||||||
|
"entity_id": "automation.litter_box_full",
|
||||||
|
"name": "Litter box full",
|
||||||
|
"edit": "/config/automation/edit/1757829250031"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
28
.storage/timer
Normal file
28
.storage/timer
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "timer",
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "night_light_1_off_timer",
|
||||||
|
"name": "night_light_1_off_timer",
|
||||||
|
"duration": "0:00:00",
|
||||||
|
"restore": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "night_light_activation_timer",
|
||||||
|
"name": "Night light activation timer",
|
||||||
|
"duration": "0:00:30",
|
||||||
|
"restore": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "stair_light_delay",
|
||||||
|
"name": "stair_light_delay",
|
||||||
|
"icon": "mdi:stairs-box",
|
||||||
|
"duration": "0:00:10",
|
||||||
|
"restore": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
6
.storage/trace.saved_traces
Normal file
6
.storage/trace.saved_traces
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"minor_version": 1,
|
||||||
|
"key": "trace.saved_traces",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
703
automations.yaml
Normal file
703
automations.yaml
Normal file
@@ -0,0 +1,703 @@
|
|||||||
|
- id: '1734313996462'
|
||||||
|
alias: Auto - Toggle Office Fan
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: a5b95319cae01f36d393a7ef03dca7d6
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- type: toggle
|
||||||
|
device_id: 8860a33d7f6298f79282948f52d3b5db
|
||||||
|
entity_id: e9b394087fd6f9eb7f1d5baddb5f93dd
|
||||||
|
domain: switch
|
||||||
|
mode: single
|
||||||
|
- id: '1734839542421'
|
||||||
|
alias: Auto - Turn On Bedroom Lamp
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: ebab4eee8659e99e53cb945fa322cde2
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- input_button.bedroom_lights
|
||||||
|
- device_id: 052358b767ecf6b2609acf2fe20f7319
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: light.lamp_lights
|
||||||
|
state: 'off'
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.standard_bedroom_lighting
|
||||||
|
mode: single
|
||||||
|
- id: '1734839572697'
|
||||||
|
alias: Auto - turn off all bedroom lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: ebab4eee8659e99e53cb945fa322cde2
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- input_button.bedroom_lights
|
||||||
|
- device_id: 052358b767ecf6b2609acf2fe20f7319
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: light.lamp_lights
|
||||||
|
state: 'on'
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bedroom_lights_off
|
||||||
|
mode: single
|
||||||
|
- id: '1734839634818'
|
||||||
|
alias: Auto - Toggle bedside Fan
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: ebab4eee8659e99e53cb945fa322cde2
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_double_press
|
||||||
|
subtype: remote_button_double_press
|
||||||
|
trigger: device
|
||||||
|
- trigger: conversation
|
||||||
|
command:
|
||||||
|
- Fan on
|
||||||
|
- Fan off
|
||||||
|
- Fan
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- type: toggle
|
||||||
|
device_id: d8478de5f4ea431ada272ac28ad7a2a0
|
||||||
|
entity_id: 52f171757001dac9fb71b7f00719b1aa
|
||||||
|
domain: switch
|
||||||
|
mode: single
|
||||||
|
- id: '1734839743730'
|
||||||
|
alias: Auto - Activate Bedtime Scene
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: ebab4eee8659e99e53cb945fa322cde2
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_long_press
|
||||||
|
subtype: remote_button_long_press
|
||||||
|
trigger: device
|
||||||
|
- trigger: conversation
|
||||||
|
command:
|
||||||
|
- Night time
|
||||||
|
- GoodNight
|
||||||
|
- 'Bed time '
|
||||||
|
- Good night
|
||||||
|
- Sleep
|
||||||
|
- Bed time
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: timer.start
|
||||||
|
target:
|
||||||
|
entity_id: timer.stair_light_delay
|
||||||
|
data:
|
||||||
|
duration: 00:00:30
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bedtime
|
||||||
|
mode: single
|
||||||
|
- id: '1734839849692'
|
||||||
|
alias: 'Auto -Turn on Bathroom lights to standard '
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: 984d2d9da0ce0b58b3ab4649bb82cb09
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
- device_id: 0e833fe5953d72d36def81f42ddec2dd
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- input_button.bathroom_lights
|
||||||
|
conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: light.master_bathroom_lights
|
||||||
|
state: 'off'
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bathroom_standard_lighting
|
||||||
|
mode: single
|
||||||
|
- id: '1734839869997'
|
||||||
|
alias: Auto - Turn off bathroom lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: 984d2d9da0ce0b58b3ab4649bb82cb09
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
- device_id: 0e833fe5953d72d36def81f42ddec2dd
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- input_button.bathroom_lights
|
||||||
|
conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: light.master_bathroom_lights
|
||||||
|
state: 'on'
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bathroom_lights_off
|
||||||
|
mode: single
|
||||||
|
- id: '1734840098396'
|
||||||
|
alias: Auto - Dim Bathroom Lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: 984d2d9da0ce0b58b3ab4649bb82cb09
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_long_press
|
||||||
|
subtype: remote_button_long_press
|
||||||
|
trigger: device
|
||||||
|
- device_id: 0e833fe5953d72d36def81f42ddec2dd
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_long_press
|
||||||
|
subtype: remote_button_long_press
|
||||||
|
trigger: device
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bathroom_dim_lighting
|
||||||
|
mode: single
|
||||||
|
- id: '1734840131118'
|
||||||
|
alias: Auto - Brighten Bathroom Lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: 984d2d9da0ce0b58b3ab4649bb82cb09
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_double_press
|
||||||
|
subtype: remote_button_double_press
|
||||||
|
trigger: device
|
||||||
|
- device_id: 0e833fe5953d72d36def81f42ddec2dd
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_double_press
|
||||||
|
subtype: remote_button_double_press
|
||||||
|
trigger: device
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bathroom_standard_lighting
|
||||||
|
mode: single
|
||||||
|
- id: '1735433657952'
|
||||||
|
alias: Auto- Toggle Office Ceiling fan
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: a5b95319cae01f36d393a7ef03dca7d6
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
- trigger: conversation
|
||||||
|
command:
|
||||||
|
- Turn on office ceiling fan
|
||||||
|
- Turn off office ceiling fan
|
||||||
|
- Office ceiling fan
|
||||||
|
- Office Fan
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- type: toggle
|
||||||
|
device_id: 2b036805d62affb4e7933ee034279bad
|
||||||
|
entity_id: 9a3c29c75ef69842b1ad5297bd7f7840
|
||||||
|
domain: switch
|
||||||
|
mode: single
|
||||||
|
- id: '1735449997148'
|
||||||
|
alias: Auto - Stair Lights Toggle
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- input_button.stair_lights_button
|
||||||
|
- device_id: 3126ead552545da0409ab0f342d6fa54
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_short_press
|
||||||
|
subtype: remote_button_short_press
|
||||||
|
trigger: device
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- type: toggle
|
||||||
|
device_id: 9f3ed8a617d6da4c6eb0a2fd412deba4
|
||||||
|
entity_id: e1817ddf4ec101285543a0f720da0869
|
||||||
|
domain: switch
|
||||||
|
mode: single
|
||||||
|
- id: '1736309562377'
|
||||||
|
alias: Battery Check
|
||||||
|
description: ''
|
||||||
|
use_blueprint:
|
||||||
|
path: sbyx/low-battery-level-detection-notification-for-all-battery-sensors.yaml
|
||||||
|
input:
|
||||||
|
actions:
|
||||||
|
- action: notify.mobile_app_cph2513
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
message: '{{sensors}} running low on battery'
|
||||||
|
- id: '1737936582270'
|
||||||
|
alias: AUTO - PausePihole5
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- input_button.piholepause5
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: rest_command.pause_ads_5
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
mode: single
|
||||||
|
- id: '1738806022551'
|
||||||
|
alias: Leak alert
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- binary_sensor.leak_sensor_1_moisture
|
||||||
|
from: 'off'
|
||||||
|
to: 'on'
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- binary_sensor.leak_sensor_2_moisture
|
||||||
|
from: 'off'
|
||||||
|
to: 'on'
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- binary_sensor.leak_sensor_3_moisture
|
||||||
|
from: 'off'
|
||||||
|
to: 'on'
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- binary_sensor.leak_sensor_4_moisture
|
||||||
|
from: 'off'
|
||||||
|
to: 'on'
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: notify.mobile_app_chris_phone
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
message: '"{{ trigger.to_state.name }} has detected a leak."'
|
||||||
|
mode: single
|
||||||
|
- id: '1738960937138'
|
||||||
|
alias: Auto - dim the lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: conversation
|
||||||
|
command: Dim the lights
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bedroom_dim_lighting
|
||||||
|
mode: single
|
||||||
|
- id: '1738961099534'
|
||||||
|
alias: Auto - toggle bedroom ceiling fan
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: conversation
|
||||||
|
command:
|
||||||
|
- Ceiling fan
|
||||||
|
- Ceiling fan on
|
||||||
|
- Ceiling fan off
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- type: toggle
|
||||||
|
device_id: 63701be14b86c1fe10d30a39b5e4b768
|
||||||
|
entity_id: d67305df23edeec936324f58be2ae39a
|
||||||
|
domain: switch
|
||||||
|
mode: single
|
||||||
|
- id: '1739747188817'
|
||||||
|
alias: Auto - Leave bedroom
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: 052358b767ecf6b2609acf2fe20f7319
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_double_press
|
||||||
|
subtype: remote_button_double_press
|
||||||
|
trigger: device
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bedroom_all_off
|
||||||
|
mode: single
|
||||||
|
- id: '1739771718054'
|
||||||
|
alias: Auto - Turn off bedroom ceiling lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: conversation
|
||||||
|
command:
|
||||||
|
- Ceiling Lights Off
|
||||||
|
- Ceiling Lights
|
||||||
|
- Ceiling Lights On
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- type: toggle
|
||||||
|
device_id: 09112e3aa200fc6c51b9d8ef8cdd1986
|
||||||
|
entity_id: dfc24301041b14afaf323e2477d9d195
|
||||||
|
domain: switch
|
||||||
|
mode: single
|
||||||
|
- id: '1741510283924'
|
||||||
|
alias: Bedtime voice command
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: conversation
|
||||||
|
command: Bedtime
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.bedtime
|
||||||
|
mode: single
|
||||||
|
- id: '1742177845418'
|
||||||
|
alias: Auto - All Bedroom Fans Off
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: conversation
|
||||||
|
command:
|
||||||
|
- Stillness
|
||||||
|
- Calm
|
||||||
|
- No Fans
|
||||||
|
- All Fans Off
|
||||||
|
- Quiet
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.all_bedroom_fans_off
|
||||||
|
mode: single
|
||||||
|
- id: '1755458947925'
|
||||||
|
alias: Auto - Daytime
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: conversation
|
||||||
|
command:
|
||||||
|
- Good Morning
|
||||||
|
- Daytime
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.daytime
|
||||||
|
mode: single
|
||||||
|
- id: '1757829250031'
|
||||||
|
alias: Litter box full
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- type: value
|
||||||
|
device_id: 9d81266973e5ff00a4e9f974e8b3b621
|
||||||
|
entity_id: a6ce899e27d1c5693573de49cb8757a1
|
||||||
|
domain: sensor
|
||||||
|
trigger: device
|
||||||
|
above: 90
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: notify.mobile_app_cph2513
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
title: Litter Robot Alert!
|
||||||
|
message: Empty the waste drawer please.
|
||||||
|
mode: single
|
||||||
|
- id: '1758413619299'
|
||||||
|
alias: Living Room Night Lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: time
|
||||||
|
at: '20:00:00'
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.living_room_evening_lights
|
||||||
|
mode: single
|
||||||
|
- id: '1758413678482'
|
||||||
|
alias: Living Room dark
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: time
|
||||||
|
at: 00:00:00
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.living_room_bedtime
|
||||||
|
mode: single
|
||||||
|
- id: '1758413851787'
|
||||||
|
alias: Manual toggle for living room lights
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- device_id: 3126ead552545da0409ab0f342d6fa54
|
||||||
|
domain: zha
|
||||||
|
type: remote_button_long_press
|
||||||
|
subtype: remote_button_long_press
|
||||||
|
trigger: device
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- choose:
|
||||||
|
- conditions:
|
||||||
|
- condition: device
|
||||||
|
type: is_off
|
||||||
|
device_id: f3e8dcade53ac6843fde79376c099a3c
|
||||||
|
entity_id: 444a937220ba5b3144e86837feda7104
|
||||||
|
domain: light
|
||||||
|
sequence:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.living_room_evening_lights
|
||||||
|
- conditions:
|
||||||
|
- condition: device
|
||||||
|
type: is_on
|
||||||
|
device_id: f3e8dcade53ac6843fde79376c099a3c
|
||||||
|
entity_id: 444a937220ba5b3144e86837feda7104
|
||||||
|
domain: light
|
||||||
|
sequence:
|
||||||
|
- action: scene.turn_on
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
target:
|
||||||
|
entity_id: scene.living_room_bedtime
|
||||||
|
mode: single
|
||||||
|
- id: '1759109671739'
|
||||||
|
alias: Synchronize states stairs b
|
||||||
|
description: ''
|
||||||
|
use_blueprint:
|
||||||
|
path: adchevrier/synchronize-the-on-off-state-of-2-entities.yaml
|
||||||
|
input:
|
||||||
|
entity_1: switch.upper_landing_b_switch
|
||||||
|
entity_2: switch.stair_light_lower_switch
|
||||||
|
- id: '1759110875124'
|
||||||
|
alias: Synchronize stairs A
|
||||||
|
description: ''
|
||||||
|
use_blueprint:
|
||||||
|
path: adchevrier/synchronize-the-on-off-state-of-2-entities.yaml
|
||||||
|
input:
|
||||||
|
entity_1: switch.inverse_of_upper_landing_switch
|
||||||
|
entity_2: switch.stair_light_upper_switch_2
|
||||||
|
- id: '1759874345238'
|
||||||
|
alias: Critical Event Reminders
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: calendar
|
||||||
|
entity_id: calendar.critical_events
|
||||||
|
event: start
|
||||||
|
offset: -00:05:00
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: notify.mobile_app_chris_phone
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
message: TTS
|
||||||
|
data:
|
||||||
|
ttl: 0
|
||||||
|
priority: high
|
||||||
|
media_stream: alarm_stream_max
|
||||||
|
tts_text: Appointment Reminder, 5 minutes, Appointment reminder, 5 minutes.
|
||||||
|
- action: notify.twilio_call
|
||||||
|
data:
|
||||||
|
message: You have an appointment please do not be late
|
||||||
|
target:
|
||||||
|
- '+12144589878'
|
||||||
|
enabled: false
|
||||||
|
mode: single
|
||||||
|
- id: '1763881743942'
|
||||||
|
alias: Unavailable entity detection & notification
|
||||||
|
description: ''
|
||||||
|
use_blueprint:
|
||||||
|
path: gmlupatelli/unavailable_entities_notification.yaml
|
||||||
|
input:
|
||||||
|
actions:
|
||||||
|
- condition: template
|
||||||
|
value_template: '{{ final_unavailable_entities | length > 0 }}'
|
||||||
|
- action: notify.mobile_app_chris_phone
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
title: Unavailable Entities Detected
|
||||||
|
message: List of entities - {{ entities }}
|
||||||
|
enabled: true
|
||||||
|
- action: persistent_notification.create
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
title: Debug Lists
|
||||||
|
message: "Excluded: {{ excluded_entities }} \nIncluded: {{ included_entities_raw
|
||||||
|
}}\nUnavailable: {{ unavailable_entities }}\nFinal: {{ final_unavailable_entities
|
||||||
|
}} \n"
|
||||||
|
enabled: false
|
||||||
|
include:
|
||||||
|
label_id: monitored
|
||||||
|
- id: '1765293318958'
|
||||||
|
alias: Master Bath Humidity
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- type: humidity
|
||||||
|
device_id: 3e9f76ced7f9bfd888f501c832f56d79
|
||||||
|
entity_id: e66f293c56bd9ee5f54565c8d15d1ea6
|
||||||
|
domain: sensor
|
||||||
|
trigger: device
|
||||||
|
above: 50
|
||||||
|
for:
|
||||||
|
hours: 0
|
||||||
|
minutes: 1
|
||||||
|
seconds: 0
|
||||||
|
conditions:
|
||||||
|
- condition: device
|
||||||
|
type: is_off
|
||||||
|
device_id: 899ae685d236a458fa035a61d30367fa
|
||||||
|
entity_id: 8641f9a2f5bf882b9feaf3ed14de0c85
|
||||||
|
domain: switch
|
||||||
|
actions:
|
||||||
|
- action: notify.mobile_app_chris_phone
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
message: Humidity sensor test!
|
||||||
|
enabled: false
|
||||||
|
- type: turn_on
|
||||||
|
device_id: 899ae685d236a458fa035a61d30367fa
|
||||||
|
entity_id: 8641f9a2f5bf882b9feaf3ed14de0c85
|
||||||
|
domain: switch
|
||||||
|
- device_id: 899ae685d236a458fa035a61d30367fa
|
||||||
|
domain: number
|
||||||
|
entity_id: 24d4c7aec0f40b0e4d92b8492dc784c2
|
||||||
|
type: set_value
|
||||||
|
value: 3600
|
||||||
|
mode: single
|
||||||
|
- id: '1765608922704'
|
||||||
|
alias: Alarmo - Pending
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- alarm_control_panel.alarmo
|
||||||
|
to:
|
||||||
|
- pending
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: notify.mobile_app_chris_phone
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
message: Pending, BEEP BEEP BEEP
|
||||||
|
mode: single
|
||||||
|
- id: '1765609013023'
|
||||||
|
alias: Alarmo - Triggered alarm
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- alarm_control_panel.alarmo
|
||||||
|
to:
|
||||||
|
- triggered
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
- action: notify.mobile_app_chris_phone
|
||||||
|
metadata: {}
|
||||||
|
data:
|
||||||
|
message: Too late!!!
|
||||||
|
mode: single
|
||||||
|
- id: '1766899102626'
|
||||||
|
alias: Alarmo - Away started
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- alarm_control_panel.alarmo
|
||||||
|
to:
|
||||||
|
- armed_away
|
||||||
|
- armed_vacation
|
||||||
|
conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: switch.cat_cam_1_privacy_mode
|
||||||
|
state:
|
||||||
|
- 'on'
|
||||||
|
actions:
|
||||||
|
- action: switch.turn_off
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id: switch.cat_cam_1_privacy_mode
|
||||||
|
data: {}
|
||||||
|
mode: single
|
||||||
|
- id: '1766899152715'
|
||||||
|
alias: Alarmo - Home/Disarmed Set
|
||||||
|
description: ''
|
||||||
|
triggers:
|
||||||
|
- trigger: state
|
||||||
|
entity_id:
|
||||||
|
- alarm_control_panel.alarmo
|
||||||
|
to:
|
||||||
|
- disarmed
|
||||||
|
- armed_home
|
||||||
|
conditions:
|
||||||
|
- condition: state
|
||||||
|
entity_id: switch.cat_cam_1_privacy_mode
|
||||||
|
state:
|
||||||
|
- 'off'
|
||||||
|
actions:
|
||||||
|
- action: switch.turn_on
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id: switch.cat_cam_1_privacy_mode
|
||||||
|
data: {}
|
||||||
|
mode: single
|
||||||
|
- id: '1767255316522'
|
||||||
|
alias: Bathroom Humidity Exhaust Fan
|
||||||
|
description: ''
|
||||||
|
use_blueprint:
|
||||||
|
path: Blackshome/bathroom-humidity-exhaust-fan.yaml
|
||||||
|
input:
|
||||||
|
trigger: sensor.master_bath_humidity
|
||||||
|
fan_switch:
|
||||||
|
entity_id: switch.master_bath_fan
|
||||||
|
include_manual_fan_switch: enable_manual_fan_switch_auto_off
|
||||||
4359
blueprints/automation/Blackshome/bathroom-humidity-exhaust-fan.yaml
Normal file
4359
blueprints/automation/Blackshome/bathroom-humidity-exhaust-fan.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
|||||||
|
blueprint:
|
||||||
|
name: Synchronize states
|
||||||
|
description: Synchronize the on/off state of 2 entities
|
||||||
|
domain: automation
|
||||||
|
input:
|
||||||
|
entity_1:
|
||||||
|
name: First entity
|
||||||
|
selector:
|
||||||
|
entity: {}
|
||||||
|
entity_2:
|
||||||
|
name: Second entity
|
||||||
|
selector:
|
||||||
|
entity: {}
|
||||||
|
source_url: https://community.home-assistant.io/t/synchronize-the-on-off-state-of-2-entities/259010
|
||||||
|
mode: restart
|
||||||
|
max_exceeded: silent
|
||||||
|
variables:
|
||||||
|
entity_1: !input entity_1
|
||||||
|
entity_2: !input entity_2
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: !input entity_1
|
||||||
|
to:
|
||||||
|
- 'off'
|
||||||
|
- 'on'
|
||||||
|
- platform: state
|
||||||
|
entity_id: !input entity_2
|
||||||
|
to:
|
||||||
|
- 'off'
|
||||||
|
- 'on'
|
||||||
|
condition:
|
||||||
|
- condition: template
|
||||||
|
value_template: '{{ states(entity_1) != states(entity_2) }}'
|
||||||
|
- condition: template
|
||||||
|
value_template: '{{ trigger.to_state.state != trigger.from_state.state }}'
|
||||||
|
- condition: template
|
||||||
|
value_template: '{{ trigger.to_state.context.parent_id is none or (trigger.to_state.context.id
|
||||||
|
!= this.context.id and trigger.to_state.context.parent_id != this.context.id)
|
||||||
|
}}'
|
||||||
|
action:
|
||||||
|
- service: homeassistant.turn_{{ trigger.to_state.state }}
|
||||||
|
data:
|
||||||
|
entity_id: '{% if trigger.from_state.entity_id == entity_1 %} {{ entity_2 }} {%
|
||||||
|
else %} {{ entity_1 }} {% endif %}'
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
blueprint:
|
||||||
|
name: Unavailable entity detection & notification
|
||||||
|
description: >
|
||||||
|
Regularly test all entities' status to check for unavailability.
|
||||||
|
Supports exclusion by entities, devices, areas, and labels for flexible filtering.
|
||||||
|
domain: automation
|
||||||
|
source_url: https://github.com/gmlupatelli/blueprints_repo/blob/master/unavailable_entities_notification/unavailable_entities_notification.yaml
|
||||||
|
input:
|
||||||
|
time:
|
||||||
|
name: Time to test on
|
||||||
|
description: Test is run at configured time
|
||||||
|
default: '10:00:00'
|
||||||
|
selector:
|
||||||
|
time: {}
|
||||||
|
monday_enabled:
|
||||||
|
name: Monday
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean: {}
|
||||||
|
tuesday_enabled:
|
||||||
|
name: Tuesday
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean: {}
|
||||||
|
wednesday_enabled:
|
||||||
|
name: Wednesday
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean: {}
|
||||||
|
thursday_enabled:
|
||||||
|
name: Thursday
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean: {}
|
||||||
|
friday_enabled:
|
||||||
|
name: Friday
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean: {}
|
||||||
|
saturday_enabled:
|
||||||
|
name: Saturday
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean: {}
|
||||||
|
sunday_enabled:
|
||||||
|
name: Sunday
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean: {}
|
||||||
|
exclude:
|
||||||
|
name: Excluded Entities
|
||||||
|
description: Entities (e.g. smartphone) to exclude. Entities, devices, areas, and labels are supported!
|
||||||
|
default: {}
|
||||||
|
selector:
|
||||||
|
target: {}
|
||||||
|
include:
|
||||||
|
name: Included Entities
|
||||||
|
description: Entities (e.g. smartphone) to include. Entities, devices, areas, and labels are supported!
|
||||||
|
default: {}
|
||||||
|
selector:
|
||||||
|
target: {}
|
||||||
|
actions:
|
||||||
|
name: Actions
|
||||||
|
description: Notifications or similar to be run. {{entities}} is replaced with a formatted list.
|
||||||
|
default: []
|
||||||
|
selector:
|
||||||
|
action: {}
|
||||||
|
|
||||||
|
variables:
|
||||||
|
monday_enabled: !input monday_enabled
|
||||||
|
tuesday_enabled: !input tuesday_enabled
|
||||||
|
wednesday_enabled: !input wednesday_enabled
|
||||||
|
thursday_enabled: !input thursday_enabled
|
||||||
|
friday_enabled: !input friday_enabled
|
||||||
|
saturday_enabled: !input saturday_enabled
|
||||||
|
sunday_enabled: !input sunday_enabled
|
||||||
|
current_day: '{{ now().weekday() | int }}'
|
||||||
|
exclude: !input exclude
|
||||||
|
include: !input include
|
||||||
|
|
||||||
|
excluded_entities: >
|
||||||
|
{% set excluded = [] %}
|
||||||
|
{% if exclude.entity_id is defined %}
|
||||||
|
{% set excluded = excluded + ( [exclude.entity_id] if exclude.entity_id is string else exclude.entity_id ) %}
|
||||||
|
{% endif %}
|
||||||
|
{% if exclude.device_id is defined %}
|
||||||
|
{% for d in ([exclude.device_id] if exclude.device_id is string else exclude.device_id) %}
|
||||||
|
{% set excluded = excluded + device_entities(d) %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if exclude.area_id is defined %}
|
||||||
|
{% for a in ([exclude.area_id] if exclude.area_id is string else exclude.area_id) %}
|
||||||
|
{% set excluded = excluded + area_entities(a) %}
|
||||||
|
{% for d in area_devices(a) %}
|
||||||
|
{% set excluded = excluded + device_entities(d) %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if exclude.label_id is defined %}
|
||||||
|
{% for l in ([exclude.label_id] if exclude.label_id is string else exclude.label_id) %}
|
||||||
|
{% set excluded = excluded + label_entities(l) %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{{ excluded }}
|
||||||
|
|
||||||
|
# Build included list
|
||||||
|
included_entities_raw: >
|
||||||
|
{{ label_entities('monitored') }}
|
||||||
|
|
||||||
|
# Collect ALL unavailable entities (no filtering yet)
|
||||||
|
unavailable_entities: >
|
||||||
|
{% set unavail = states | selectattr('state','eq','unavailable') | map(attribute='entity_id') | list %}
|
||||||
|
{{ unavail }}
|
||||||
|
|
||||||
|
# Apply include/exclude filtering only here
|
||||||
|
final_unavailable_entities: >
|
||||||
|
{% set ns = namespace(final=[]) %}
|
||||||
|
{% set included_list = included_entities_raw %}
|
||||||
|
{% set excluded_list = excluded_entities %}
|
||||||
|
{% for entity in unavailable_entities %}
|
||||||
|
{% set dev_id = device_id(entity) %}
|
||||||
|
{% if entity in included_list and not entity in excluded_list and (not device_attr(dev_id,'disabled_by')) %}
|
||||||
|
{% set ns.final = ns.final + [state_attr(entity,'friendly_name') ~ ' (' ~ entity ~ ')'] %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ ns.final }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
entities: "{{ '- ' }}{{ final_unavailable_entities | join('\n- ') }}"
|
||||||
|
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: !input time
|
||||||
|
|
||||||
|
condition:
|
||||||
|
- condition: template
|
||||||
|
value_template: >
|
||||||
|
{{
|
||||||
|
(current_day == 0 and monday_enabled) or
|
||||||
|
(current_day == 1 and tuesday_enabled) or
|
||||||
|
(current_day == 2 and wednesday_enabled) or
|
||||||
|
(current_day == 3 and thursday_enabled) or
|
||||||
|
(current_day == 4 and friday_enabled) or
|
||||||
|
(current_day == 5 and saturday_enabled) or
|
||||||
|
(current_day == 6 and sunday_enabled)
|
||||||
|
}}
|
||||||
|
- condition: template
|
||||||
|
value_template: '{{ final_unavailable_entities | length > 0 }}'
|
||||||
|
|
||||||
|
|
||||||
|
action: !input actions
|
||||||
|
|
||||||
|
mode: single
|
||||||
|
max_exceeded: silent
|
||||||
58
blueprints/automation/homeassistant/motion_light.yaml
Normal file
58
blueprints/automation/homeassistant/motion_light.yaml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
blueprint:
|
||||||
|
name: Motion-activated Light
|
||||||
|
description: Turn on a light when motion is detected.
|
||||||
|
domain: automation
|
||||||
|
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml
|
||||||
|
author: Home Assistant
|
||||||
|
input:
|
||||||
|
motion_entity:
|
||||||
|
name: Motion Sensor
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
filter:
|
||||||
|
- device_class: occupancy
|
||||||
|
domain: binary_sensor
|
||||||
|
- device_class: motion
|
||||||
|
domain: binary_sensor
|
||||||
|
light_target:
|
||||||
|
name: Light
|
||||||
|
selector:
|
||||||
|
target:
|
||||||
|
entity:
|
||||||
|
domain: light
|
||||||
|
no_motion_wait:
|
||||||
|
name: Wait time
|
||||||
|
description: Time to leave the light on after last motion is detected.
|
||||||
|
default: 120
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0
|
||||||
|
max: 3600
|
||||||
|
unit_of_measurement: seconds
|
||||||
|
|
||||||
|
# If motion is detected within the delay,
|
||||||
|
# we restart the script.
|
||||||
|
mode: restart
|
||||||
|
max_exceeded: silent
|
||||||
|
|
||||||
|
triggers:
|
||||||
|
trigger: state
|
||||||
|
entity_id: !input motion_entity
|
||||||
|
from: "off"
|
||||||
|
to: "on"
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- alias: "Turn on the light"
|
||||||
|
action: light.turn_on
|
||||||
|
target: !input light_target
|
||||||
|
- alias: "Wait until there is no motion from device"
|
||||||
|
wait_for_trigger:
|
||||||
|
trigger: state
|
||||||
|
entity_id: !input motion_entity
|
||||||
|
from: "on"
|
||||||
|
to: "off"
|
||||||
|
- alias: "Wait the number of seconds that has been set"
|
||||||
|
delay: !input no_motion_wait
|
||||||
|
- alias: "Turn off the light"
|
||||||
|
action: light.turn_off
|
||||||
|
target: !input light_target
|
||||||
50
blueprints/automation/homeassistant/notify_leaving_zone.yaml
Normal file
50
blueprints/automation/homeassistant/notify_leaving_zone.yaml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
blueprint:
|
||||||
|
name: Zone Notification
|
||||||
|
description: Send a notification to a device when a person leaves a specific zone.
|
||||||
|
domain: automation
|
||||||
|
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml
|
||||||
|
author: Home Assistant
|
||||||
|
input:
|
||||||
|
person_entity:
|
||||||
|
name: Person
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
filter:
|
||||||
|
domain: person
|
||||||
|
zone_entity:
|
||||||
|
name: Zone
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
filter:
|
||||||
|
domain: zone
|
||||||
|
notify_device:
|
||||||
|
name: Device to notify
|
||||||
|
description: Device needs to run the official Home Assistant app to receive notifications.
|
||||||
|
selector:
|
||||||
|
device:
|
||||||
|
filter:
|
||||||
|
integration: mobile_app
|
||||||
|
|
||||||
|
triggers:
|
||||||
|
trigger: state
|
||||||
|
entity_id: !input person_entity
|
||||||
|
|
||||||
|
variables:
|
||||||
|
zone_entity: !input zone_entity
|
||||||
|
# This is the state of the person when it's in this zone.
|
||||||
|
zone_state: "{{ states[zone_entity].name }}"
|
||||||
|
person_entity: !input person_entity
|
||||||
|
person_name: "{{ states[person_entity].name }}"
|
||||||
|
|
||||||
|
conditions:
|
||||||
|
condition: template
|
||||||
|
# The first case handles leaving the Home zone which has a special state when zoning called 'home'.
|
||||||
|
# The second case handles leaving all other zones.
|
||||||
|
value_template: "{{ zone_entity == 'zone.home' and trigger.from_state.state == 'home' and trigger.to_state.state != 'home' or trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}"
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- alias: "Notify that a person has left the zone"
|
||||||
|
domain: mobile_app
|
||||||
|
type: notify
|
||||||
|
device_id: !input notify_device
|
||||||
|
message: "{{ person_name }} has left {{ zone_state }}"
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
blueprint:
|
||||||
|
name: Low battery level detection & notification for all battery sensors
|
||||||
|
description: Regularly test all sensors with 'battery' device-class for crossing
|
||||||
|
a certain battery level threshold and if so execute an action.
|
||||||
|
domain: automation
|
||||||
|
input:
|
||||||
|
threshold:
|
||||||
|
name: Battery warning level threshold
|
||||||
|
description: Battery sensors below threshold are assumed to be low-battery (as
|
||||||
|
well as binary battery sensors with value 'on').
|
||||||
|
default: 20
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 5.0
|
||||||
|
max: 100.0
|
||||||
|
unit_of_measurement: '%'
|
||||||
|
mode: slider
|
||||||
|
step: 5.0
|
||||||
|
time:
|
||||||
|
name: Time to test on
|
||||||
|
description: Test is run at configured time
|
||||||
|
default: '10:00:00'
|
||||||
|
selector:
|
||||||
|
time: {}
|
||||||
|
day:
|
||||||
|
name: Weekday to test on
|
||||||
|
description: 'Test is run at configured time either everyday (0) or on a given
|
||||||
|
weekday (1: Monday ... 7: Sunday)'
|
||||||
|
default: 0
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0.0
|
||||||
|
max: 7.0
|
||||||
|
mode: slider
|
||||||
|
step: 1.0
|
||||||
|
exclude:
|
||||||
|
name: Excluded Sensors
|
||||||
|
description: Battery sensors (e.g. smartphone) to exclude from detection. Only
|
||||||
|
entities are supported, devices must be expanded!
|
||||||
|
default:
|
||||||
|
entity_id: []
|
||||||
|
selector:
|
||||||
|
target:
|
||||||
|
entity:
|
||||||
|
- device_class:
|
||||||
|
- battery
|
||||||
|
actions:
|
||||||
|
name: Actions
|
||||||
|
description: Notifications or similar to be run. {{sensors}} is replaced with
|
||||||
|
the names of sensors being low on battery.
|
||||||
|
selector:
|
||||||
|
action: {}
|
||||||
|
source_url: https://gist.github.com/sbyx/1f6f434f0903b872b84c4302637d0890
|
||||||
|
variables:
|
||||||
|
day: !input day
|
||||||
|
threshold: !input threshold
|
||||||
|
exclude: !input exclude
|
||||||
|
sensors: "{% set result = namespace(sensors=[]) %} {% for state in states.sensor
|
||||||
|
| selectattr('attributes.device_class', '==', 'battery') %}\n {% if 0 <= state.state
|
||||||
|
| int(-1) < threshold | int and not state.entity_id in exclude.entity_id %}\n
|
||||||
|
\ {% set result.sensors = result.sensors + [state.name ~ ' (' ~ state.state
|
||||||
|
~ ' %)'] %}\n {% endif %}\n{% endfor %} {% for state in states.binary_sensor
|
||||||
|
| selectattr('attributes.device_class', '==', 'battery') | selectattr('state',
|
||||||
|
'==', 'on') %}\n {% if not state.entity_id in exclude.entity_id %}\n {% set
|
||||||
|
result.sensors = result.sensors + [state.name] %}\n {% endif %}\n{% endfor %}
|
||||||
|
{{result.sensors|join(', ')}}"
|
||||||
|
trigger:
|
||||||
|
- platform: time
|
||||||
|
at: !input time
|
||||||
|
condition:
|
||||||
|
- '{{ sensors != '''' and (day | int == 0 or day | int == now().isoweekday()) }}'
|
||||||
|
action:
|
||||||
|
- choose: []
|
||||||
|
default: !input actions
|
||||||
|
mode: single
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
blueprint:
|
||||||
|
name: Confirmable Notification
|
||||||
|
description: >-
|
||||||
|
A script that sends an actionable notification with a confirmation before
|
||||||
|
running the specified action.
|
||||||
|
domain: script
|
||||||
|
source_url: https://github.com/home-assistant/core/blob/master/homeassistant/components/script/blueprints/confirmable_notification.yaml
|
||||||
|
author: Home Assistant
|
||||||
|
input:
|
||||||
|
notify_device:
|
||||||
|
name: Device to notify
|
||||||
|
description: Device needs to run the official Home Assistant app to receive notifications.
|
||||||
|
selector:
|
||||||
|
device:
|
||||||
|
filter:
|
||||||
|
integration: mobile_app
|
||||||
|
title:
|
||||||
|
name: "Title"
|
||||||
|
description: "The title of the button shown in the notification."
|
||||||
|
default: ""
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
message:
|
||||||
|
name: "Message"
|
||||||
|
description: "The message body"
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
confirm_text:
|
||||||
|
name: "Confirmation Text"
|
||||||
|
description: "Text to show on the confirmation button"
|
||||||
|
default: "Confirm"
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
confirm_action:
|
||||||
|
name: "Confirmation Action"
|
||||||
|
description: "Action to run when notification is confirmed"
|
||||||
|
default: []
|
||||||
|
selector:
|
||||||
|
action:
|
||||||
|
dismiss_text:
|
||||||
|
name: "Dismiss Text"
|
||||||
|
description: "Text to show on the dismiss button"
|
||||||
|
default: "Dismiss"
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
dismiss_action:
|
||||||
|
name: "Dismiss Action"
|
||||||
|
description: "Action to run when notification is dismissed"
|
||||||
|
default: []
|
||||||
|
selector:
|
||||||
|
action:
|
||||||
|
|
||||||
|
mode: restart
|
||||||
|
|
||||||
|
sequence:
|
||||||
|
- alias: "Set up variables"
|
||||||
|
variables:
|
||||||
|
action_confirm: "{{ 'CONFIRM_' ~ context.id }}"
|
||||||
|
action_dismiss: "{{ 'DISMISS_' ~ context.id }}"
|
||||||
|
- alias: "Send notification"
|
||||||
|
domain: mobile_app
|
||||||
|
type: notify
|
||||||
|
device_id: !input notify_device
|
||||||
|
title: !input title
|
||||||
|
message: !input message
|
||||||
|
data:
|
||||||
|
actions:
|
||||||
|
- action: "{{ action_confirm }}"
|
||||||
|
title: !input confirm_text
|
||||||
|
- action: "{{ action_dismiss }}"
|
||||||
|
title: !input dismiss_text
|
||||||
|
- alias: "Awaiting response"
|
||||||
|
wait_for_trigger:
|
||||||
|
- platform: event
|
||||||
|
event_type: mobile_app_notification_action
|
||||||
|
event_data:
|
||||||
|
action: "{{ action_confirm }}"
|
||||||
|
- platform: event
|
||||||
|
event_type: mobile_app_notification_action
|
||||||
|
event_data:
|
||||||
|
action: "{{ action_dismiss }}"
|
||||||
|
- choose:
|
||||||
|
- conditions: "{{ wait.trigger.event.data.action == action_confirm }}"
|
||||||
|
sequence: !input confirm_action
|
||||||
|
- conditions: "{{ wait.trigger.event.data.action == action_dismiss }}"
|
||||||
|
sequence: !input dismiss_action
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
blueprint:
|
||||||
|
name: Invert a binary sensor
|
||||||
|
description: Creates a binary_sensor which holds the inverted value of a reference binary_sensor
|
||||||
|
domain: template
|
||||||
|
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/template/blueprints/inverted_binary_sensor.yaml
|
||||||
|
input:
|
||||||
|
reference_entity:
|
||||||
|
name: Binary sensor to be inverted
|
||||||
|
description: The binary_sensor which needs to have its value inverted
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
domain: binary_sensor
|
||||||
|
variables:
|
||||||
|
reference_entity: !input reference_entity
|
||||||
|
binary_sensor:
|
||||||
|
state: >
|
||||||
|
{% if states(reference_entity) == 'on' %}
|
||||||
|
off
|
||||||
|
{% elif states(reference_entity) == 'off' %}
|
||||||
|
on
|
||||||
|
{% else %}
|
||||||
|
{{ states(reference_entity) }}
|
||||||
|
{% endif %}
|
||||||
|
# delay_on: not_used in this example
|
||||||
|
# delay_off: not_used in this example
|
||||||
|
# auto_off: not_used in this example
|
||||||
|
availability: "{{ states(reference_entity) not in ('unknown', 'unavailable') }}"
|
||||||
173
configuration.yaml
Normal file
173
configuration.yaml
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
|
||||||
|
# Loads default set of integrations. Do not remove.
|
||||||
|
default_config:
|
||||||
|
homeassistant:
|
||||||
|
auth_mfa_modules:
|
||||||
|
- type: totp
|
||||||
|
# Load frontend themes from the themes folder
|
||||||
|
frontend:
|
||||||
|
themes: !include_dir_merge_named themes
|
||||||
|
template:
|
||||||
|
- switch:
|
||||||
|
- name: "Inverse of Upper Landing Switch"
|
||||||
|
unique_id: inverse_Upper_Landing_A
|
||||||
|
state: >
|
||||||
|
{{ is_state('switch.upper_landing_a_switch', 'off') }}
|
||||||
|
turn_on:
|
||||||
|
service: switch.turn_off
|
||||||
|
target:
|
||||||
|
entity_id: switch.upper_landing_a_switch
|
||||||
|
turn_off:
|
||||||
|
service: switch.turn_on
|
||||||
|
target:
|
||||||
|
entity_id: switch.upper_landing_a_switch
|
||||||
|
|
||||||
|
automation: !include automations.yaml
|
||||||
|
script: !include scripts.yaml
|
||||||
|
scene: !include scenes.yaml
|
||||||
|
rest_command:
|
||||||
|
pause_ads_5:
|
||||||
|
url: "http://192.168.0.248:20720/admin/api.php?disable=300&auth=c874330af40074c580efdb36d0476e216d3bc3ffb860dcfd9361da5a457770f5"
|
||||||
|
panel_custom:
|
||||||
|
- name: panel_automations
|
||||||
|
sidebar_title: Automations
|
||||||
|
sidebar_icon: mdi:cogs
|
||||||
|
url_path: 'config/automation/dashboard'
|
||||||
|
module_url: /api/hassio/app/entrypoint.js
|
||||||
|
embed_iframe: true
|
||||||
|
require_admin: true
|
||||||
|
- name: panel_integrations
|
||||||
|
sidebar_title: Integrations
|
||||||
|
sidebar_icon: mdi:cogs
|
||||||
|
url_path: 'config/integrations/dashboard'
|
||||||
|
module_url: /api/hassio/app/entrypoint.js
|
||||||
|
embed_iframe: true
|
||||||
|
require_admin: true
|
||||||
|
- name: panel_devices
|
||||||
|
sidebar_title: Devices
|
||||||
|
sidebar_icon: mdi:cogs
|
||||||
|
url_path: 'config/devices/dashboard'
|
||||||
|
module_url: /api/hassio/app/entrypoint.js
|
||||||
|
embed_iframe: true
|
||||||
|
require_admin: true
|
||||||
|
http:
|
||||||
|
use_x_forwarded_for: true
|
||||||
|
trusted_proxies:
|
||||||
|
- 192.168.0.249
|
||||||
|
twilio:
|
||||||
|
account_sid: "AC7732cb92a1dc8b59749e8d695495793c"
|
||||||
|
auth_token: "9ec61ee9906106e8597f6151df910466"
|
||||||
|
notify:
|
||||||
|
- name: twilio_call
|
||||||
|
platform: twilio_call
|
||||||
|
from_number: "+16828463062"
|
||||||
|
# Groups
|
||||||
|
light:
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_PRIMARY_BEDROOM_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_PRIMARY_BATH_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_PRIMARY_CLOSET_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_ENTRY_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_GARAGE_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_KITCHEN_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_LIVING_ROOM_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_POWDER_BATH_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_OFFICE_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_STAIR_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_PATIO_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_LAUNDRY_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_GARAGE_ENTRY_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_PANTRY_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_STORAGE_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_PRIMARY_TOILET_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_LIBRARY_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_GUEST_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_MOVIE_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_GUEST_BATH_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_UPSTAIRS_HALL_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
|
|
||||||
|
- platform: group
|
||||||
|
name: "_NH_LOFT_LIGHTS"
|
||||||
|
entities:
|
||||||
|
- light.lamp_lights
|
||||||
578
custom_components/alarmo/__init__.py
Normal file
578
custom_components/alarmo/__init__.py
Normal file
@@ -0,0 +1,578 @@
|
|||||||
|
"""The Alarmo Integration."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import concurrent.futures
|
||||||
|
|
||||||
|
import bcrypt
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
asyncio,
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_CODE,
|
||||||
|
ATTR_NAME,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.helpers.service import (
|
||||||
|
async_register_admin_service,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
async_dispatcher_send,
|
||||||
|
async_dispatcher_connect,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||||
|
from homeassistant.components.alarm_control_panel import DOMAIN as PLATFORM
|
||||||
|
|
||||||
|
from . import const
|
||||||
|
from .card import async_register_card
|
||||||
|
from .mqtt import MqttHandler
|
||||||
|
from .event import EventHandler
|
||||||
|
from .panel import (
|
||||||
|
async_register_panel,
|
||||||
|
async_unregister_panel,
|
||||||
|
)
|
||||||
|
from .store import async_get_registry
|
||||||
|
from .sensors import (
|
||||||
|
ATTR_GROUP,
|
||||||
|
ATTR_ENTITIES,
|
||||||
|
ATTR_NEW_ENTITY_ID,
|
||||||
|
SensorHandler,
|
||||||
|
)
|
||||||
|
from .websockets import async_register_websockets
|
||||||
|
from .automations import AutomationHandler
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Max number of threads to start when checking user codes.
|
||||||
|
MAX_WORKERS = 4
|
||||||
|
# Number of rounds of hashing when computing user hashes.
|
||||||
|
BCRYPT_NUM_ROUNDS = 10
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass, config):
|
||||||
|
"""Track states and offer events for sensors."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
|
"""Set up Alarmo integration from a config entry."""
|
||||||
|
session = async_get_clientsession(hass)
|
||||||
|
|
||||||
|
store = await async_get_registry(hass)
|
||||||
|
coordinator = AlarmoCoordinator(hass, session, entry, store)
|
||||||
|
|
||||||
|
device_registry = dr.async_get(hass)
|
||||||
|
device_registry.async_get_or_create(
|
||||||
|
config_entry_id=entry.entry_id,
|
||||||
|
identifiers={(const.DOMAIN, coordinator.id)},
|
||||||
|
name=const.NAME,
|
||||||
|
model=const.NAME,
|
||||||
|
sw_version=const.VERSION,
|
||||||
|
manufacturer=const.MANUFACTURER,
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.data.setdefault(const.DOMAIN, {})
|
||||||
|
hass.data[const.DOMAIN] = {"coordinator": coordinator, "areas": {}, "master": None}
|
||||||
|
|
||||||
|
if entry.unique_id is None:
|
||||||
|
hass.config_entries.async_update_entry(entry, unique_id=coordinator.id, data={})
|
||||||
|
|
||||||
|
await hass.config_entries.async_forward_entry_setups(entry, [PLATFORM])
|
||||||
|
|
||||||
|
# Register the panel (frontend)
|
||||||
|
await async_register_panel(hass)
|
||||||
|
await async_register_card(hass)
|
||||||
|
|
||||||
|
# Websocket support
|
||||||
|
await async_register_websockets(hass)
|
||||||
|
|
||||||
|
# Register custom services
|
||||||
|
register_services(hass)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass, entry):
|
||||||
|
"""Unload Alarmo config entry."""
|
||||||
|
unload_ok = all(
|
||||||
|
await asyncio.gather(
|
||||||
|
*[hass.config_entries.async_forward_entry_unload(entry, PLATFORM)]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not unload_ok:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async_unregister_panel(hass)
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
await coordinator.async_unload()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_remove_entry(hass, entry):
|
||||||
|
"""Remove Alarmo config entry."""
|
||||||
|
async_unregister_panel(hass)
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
await coordinator.async_delete_config()
|
||||||
|
del hass.data[const.DOMAIN]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Handle migration of config entry."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoCoordinator(DataUpdateCoordinator):
|
||||||
|
"""Define an object to hold Alarmo device."""
|
||||||
|
|
||||||
|
def __init__(self, hass, session, entry, store):
|
||||||
|
"""Initialize."""
|
||||||
|
self.id = entry.unique_id
|
||||||
|
self.hass = hass
|
||||||
|
self.entry = entry
|
||||||
|
self.store = store
|
||||||
|
self._subscriptions = []
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
hass, "alarmo_platform_loaded", self.setup_alarm_entities
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.register_events()
|
||||||
|
|
||||||
|
super().__init__(hass, _LOGGER, config_entry=entry, name=const.DOMAIN)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def setup_alarm_entities(self):
|
||||||
|
"""Set up alarm_control_panel entities based on areas in storage."""
|
||||||
|
self.hass.data[const.DOMAIN]["sensor_handler"] = SensorHandler(self.hass)
|
||||||
|
self.hass.data[const.DOMAIN]["automation_handler"] = AutomationHandler(
|
||||||
|
self.hass
|
||||||
|
)
|
||||||
|
self.hass.data[const.DOMAIN]["mqtt_handler"] = MqttHandler(self.hass)
|
||||||
|
self.hass.data[const.DOMAIN]["event_handler"] = EventHandler(self.hass)
|
||||||
|
|
||||||
|
areas = self.store.async_get_areas()
|
||||||
|
config = self.store.async_get_config()
|
||||||
|
|
||||||
|
for item in areas.values():
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_register_entity", item)
|
||||||
|
|
||||||
|
if len(areas) > 1 and config["master"]["enabled"]:
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_register_master", config["master"])
|
||||||
|
|
||||||
|
async def async_update_config(self, data):
|
||||||
|
"""Update the main configuration."""
|
||||||
|
if "master" in data:
|
||||||
|
old_config = self.store.async_get_config()
|
||||||
|
if old_config[const.ATTR_MASTER] != data["master"]:
|
||||||
|
if self.hass.data[const.DOMAIN]["master"]:
|
||||||
|
await self.async_remove_entity("master")
|
||||||
|
if data["master"]["enabled"]:
|
||||||
|
async_dispatcher_send(
|
||||||
|
self.hass, "alarmo_register_master", data["master"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
automations = self.hass.data[const.DOMAIN][
|
||||||
|
"automation_handler"
|
||||||
|
].get_automations_by_area(None)
|
||||||
|
if len(automations):
|
||||||
|
for el in automations:
|
||||||
|
self.store.async_delete_automation(el)
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_automations_updated")
|
||||||
|
|
||||||
|
self.store.async_update_config(data)
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_config_updated")
|
||||||
|
|
||||||
|
async def async_update_area_config(
|
||||||
|
self,
|
||||||
|
area_id: str | None = None,
|
||||||
|
data: dict = {},
|
||||||
|
):
|
||||||
|
"""Update area configuration."""
|
||||||
|
if const.ATTR_REMOVE in data:
|
||||||
|
# delete an area
|
||||||
|
res = self.store.async_get_area(area_id)
|
||||||
|
if not res:
|
||||||
|
return
|
||||||
|
sensors = self.store.async_get_sensors()
|
||||||
|
sensors = dict(filter(lambda el: el[1]["area"] == area_id, sensors.items()))
|
||||||
|
if sensors:
|
||||||
|
for el in sensors.keys():
|
||||||
|
self.store.async_delete_sensor(el)
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_sensors_updated")
|
||||||
|
|
||||||
|
automations = self.hass.data[const.DOMAIN][
|
||||||
|
"automation_handler"
|
||||||
|
].get_automations_by_area(area_id)
|
||||||
|
if len(automations):
|
||||||
|
for el in automations:
|
||||||
|
self.store.async_delete_automation(el)
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_automations_updated")
|
||||||
|
|
||||||
|
self.store.async_delete_area(area_id)
|
||||||
|
await self.async_remove_entity(area_id)
|
||||||
|
|
||||||
|
if (
|
||||||
|
len(self.store.async_get_areas()) == 1
|
||||||
|
and self.hass.data[const.DOMAIN]["master"]
|
||||||
|
):
|
||||||
|
await self.async_remove_entity("master")
|
||||||
|
|
||||||
|
elif self.store.async_get_area(area_id):
|
||||||
|
# modify an area
|
||||||
|
entry = self.store.async_update_area(area_id, data)
|
||||||
|
if "name" not in data:
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_config_updated", area_id)
|
||||||
|
else:
|
||||||
|
await self.async_remove_entity(area_id)
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_register_entity", entry)
|
||||||
|
else:
|
||||||
|
# create an area
|
||||||
|
entry = self.store.async_create_area(data)
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_register_entity", entry)
|
||||||
|
|
||||||
|
config = self.store.async_get_config()
|
||||||
|
|
||||||
|
if len(self.store.async_get_areas()) == 2 and config["master"]["enabled"]:
|
||||||
|
async_dispatcher_send(
|
||||||
|
self.hass, "alarmo_register_master", config["master"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def async_update_sensor_config(self, entity_id: str, data: dict):
|
||||||
|
"""Update sensor configuration."""
|
||||||
|
group = None
|
||||||
|
if ATTR_GROUP in data:
|
||||||
|
group = data[ATTR_GROUP]
|
||||||
|
del data[ATTR_GROUP]
|
||||||
|
|
||||||
|
if ATTR_NEW_ENTITY_ID in data:
|
||||||
|
# delete old sensor entry when changing the entity_id
|
||||||
|
new_entity_id = data[ATTR_NEW_ENTITY_ID]
|
||||||
|
del data[ATTR_NEW_ENTITY_ID]
|
||||||
|
self.store.async_delete_sensor(entity_id)
|
||||||
|
self.assign_sensor_to_group(new_entity_id, group)
|
||||||
|
self.assign_sensor_to_group(entity_id, None)
|
||||||
|
entity_id = new_entity_id
|
||||||
|
|
||||||
|
if const.ATTR_REMOVE in data:
|
||||||
|
self.store.async_delete_sensor(entity_id)
|
||||||
|
self.assign_sensor_to_group(entity_id, None)
|
||||||
|
elif self.store.async_get_sensor(entity_id):
|
||||||
|
self.store.async_update_sensor(entity_id, data)
|
||||||
|
self.assign_sensor_to_group(entity_id, group)
|
||||||
|
else:
|
||||||
|
self.store.async_create_sensor(entity_id, data)
|
||||||
|
self.assign_sensor_to_group(entity_id, group)
|
||||||
|
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_sensors_updated")
|
||||||
|
|
||||||
|
def _validate_user_code(self, user_id: str, data: dict):
|
||||||
|
user_with_code = self.async_authenticate_user(data[ATTR_CODE])
|
||||||
|
if user_id:
|
||||||
|
if const.ATTR_OLD_CODE not in data:
|
||||||
|
return "No code provided"
|
||||||
|
if not self.async_authenticate_user(data[const.ATTR_OLD_CODE], user_id):
|
||||||
|
return "Wrong code provided"
|
||||||
|
if user_with_code and user_with_code[const.ATTR_USER_ID] != user_id:
|
||||||
|
return "User with same code already exists"
|
||||||
|
elif user_with_code:
|
||||||
|
return "User with same code already exists"
|
||||||
|
return
|
||||||
|
|
||||||
|
def _validate_user_name(self, user_id: str, data: dict):
|
||||||
|
if not data[ATTR_NAME]:
|
||||||
|
return "User name must not be empty"
|
||||||
|
for user in self.store.async_get_users().values():
|
||||||
|
if (
|
||||||
|
data[ATTR_NAME] == user[ATTR_NAME]
|
||||||
|
and user_id != user[const.ATTR_USER_ID]
|
||||||
|
):
|
||||||
|
return "User with same name already exists"
|
||||||
|
return
|
||||||
|
|
||||||
|
def async_update_user_config(self, user_id: str | None = None, data: dict = {}):
|
||||||
|
"""Update user configuration."""
|
||||||
|
if const.ATTR_REMOVE in data:
|
||||||
|
self.store.async_delete_user(user_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if ATTR_NAME in data:
|
||||||
|
err = self._validate_user_name(user_id, data)
|
||||||
|
if err:
|
||||||
|
_LOGGER.error(err)
|
||||||
|
return err
|
||||||
|
if ATTR_CODE in data:
|
||||||
|
err = self._validate_user_code(user_id, data)
|
||||||
|
if err:
|
||||||
|
_LOGGER.error(err)
|
||||||
|
return err
|
||||||
|
|
||||||
|
if data.get(ATTR_CODE):
|
||||||
|
data[const.ATTR_CODE_FORMAT] = (
|
||||||
|
"number" if data[ATTR_CODE].isdigit() else "text"
|
||||||
|
)
|
||||||
|
data[const.ATTR_CODE_LENGTH] = len(data[ATTR_CODE])
|
||||||
|
hashed = bcrypt.hashpw(
|
||||||
|
data[ATTR_CODE].encode("utf-8"),
|
||||||
|
bcrypt.gensalt(rounds=BCRYPT_NUM_ROUNDS),
|
||||||
|
)
|
||||||
|
hashed = base64.b64encode(hashed)
|
||||||
|
data[ATTR_CODE] = hashed.decode()
|
||||||
|
|
||||||
|
if not user_id:
|
||||||
|
self.store.async_create_user(data)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if ATTR_CODE in data:
|
||||||
|
del data[const.ATTR_OLD_CODE]
|
||||||
|
self.store.async_update_user(user_id, data)
|
||||||
|
return
|
||||||
|
|
||||||
|
def async_authenticate_user(self, code: str, user_id: str | None = None):
|
||||||
|
"""Authenticate a user by code."""
|
||||||
|
|
||||||
|
def check_user_code(user, code):
|
||||||
|
"""Returns the supplied user object if the code matches, None otherwise."""
|
||||||
|
if not user[const.ATTR_ENABLED]:
|
||||||
|
return
|
||||||
|
elif not user[ATTR_CODE] and not code:
|
||||||
|
return user
|
||||||
|
elif user[ATTR_CODE]:
|
||||||
|
hash = base64.b64decode(user[ATTR_CODE])
|
||||||
|
if bcrypt.checkpw(code.encode("utf-8"), hash):
|
||||||
|
return user
|
||||||
|
|
||||||
|
if user_id:
|
||||||
|
return check_user_code(self.store.async_get_user(user_id), code)
|
||||||
|
|
||||||
|
users = self.store.async_get_users()
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
|
||||||
|
futures = [
|
||||||
|
executor.submit(check_user_code, user, code) for user in users.values()
|
||||||
|
]
|
||||||
|
for future in concurrent.futures.as_completed(futures):
|
||||||
|
if future.result():
|
||||||
|
executor.shutdown(wait=False, cancel_futures=True)
|
||||||
|
return future.result()
|
||||||
|
|
||||||
|
def async_update_automation_config(
|
||||||
|
self,
|
||||||
|
automation_id: str | None = None,
|
||||||
|
data: dict = {},
|
||||||
|
):
|
||||||
|
"""Update automation configuration."""
|
||||||
|
if const.ATTR_REMOVE in data:
|
||||||
|
self.store.async_delete_automation(automation_id)
|
||||||
|
elif not automation_id:
|
||||||
|
self.store.async_create_automation(data)
|
||||||
|
else:
|
||||||
|
self.store.async_update_automation(automation_id, data)
|
||||||
|
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_automations_updated")
|
||||||
|
|
||||||
|
def register_events(self):
|
||||||
|
"""Register event handlers."""
|
||||||
|
|
||||||
|
# handle push notifications with action buttons
|
||||||
|
@callback
|
||||||
|
async def async_handle_push_event(event):
|
||||||
|
if not event.data:
|
||||||
|
return
|
||||||
|
action = (
|
||||||
|
event.data.get("actionName")
|
||||||
|
if "actionName" in event.data
|
||||||
|
else event.data.get("action")
|
||||||
|
)
|
||||||
|
|
||||||
|
if action not in const.EVENT_ACTIONS:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.hass.data[const.DOMAIN]["master"]:
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["master"]
|
||||||
|
elif len(self.hass.data[const.DOMAIN]["areas"]) == 1:
|
||||||
|
alarm_entity = next(
|
||||||
|
iter(self.hass.data[const.DOMAIN]["areas"].values())
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_LOGGER.info(
|
||||||
|
"Cannot process the push action, since there are multiple areas."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
arm_mode = (
|
||||||
|
alarm_entity._revert_state
|
||||||
|
if alarm_entity._revert_state in const.ARM_MODES
|
||||||
|
else alarm_entity._arm_mode
|
||||||
|
)
|
||||||
|
res = re.search(r"^ALARMO_ARM_", action)
|
||||||
|
if res:
|
||||||
|
arm_mode = action.replace("ALARMO_", "").lower().replace("arm", "armed")
|
||||||
|
if not arm_mode:
|
||||||
|
_LOGGER.info(
|
||||||
|
"Cannot process the push action, since the arm mode is not known."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if action == const.EVENT_ACTION_FORCE_ARM:
|
||||||
|
_LOGGER.info("Received request for force arming")
|
||||||
|
alarm_entity.async_handle_arm_request(
|
||||||
|
arm_mode, skip_code=True, bypass_open_sensors=True
|
||||||
|
)
|
||||||
|
elif action == const.EVENT_ACTION_RETRY_ARM:
|
||||||
|
_LOGGER.info("Received request for retry arming")
|
||||||
|
alarm_entity.async_handle_arm_request(arm_mode, skip_code=True)
|
||||||
|
elif action == const.EVENT_ACTION_DISARM:
|
||||||
|
_LOGGER.info("Received request for disarming")
|
||||||
|
alarm_entity.alarm_disarm(None, skip_code=True)
|
||||||
|
else:
|
||||||
|
_LOGGER.info(
|
||||||
|
"Received request for arming with mode %s",
|
||||||
|
arm_mode,
|
||||||
|
)
|
||||||
|
alarm_entity.async_handle_arm_request(arm_mode, skip_code=True)
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
self.hass.bus.async_listen(const.PUSH_EVENT, async_handle_push_event)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_remove_entity(self, area_id: str):
|
||||||
|
"""Remove an alarm_control_panel entity."""
|
||||||
|
entity_registry = er.async_get(self.hass)
|
||||||
|
if area_id == "master":
|
||||||
|
entity = self.hass.data[const.DOMAIN]["master"]
|
||||||
|
entity_registry.async_remove(entity.entity_id)
|
||||||
|
self.hass.data[const.DOMAIN]["master"] = None
|
||||||
|
else:
|
||||||
|
entity = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
entity_registry.async_remove(entity.entity_id)
|
||||||
|
self.hass.data[const.DOMAIN]["areas"].pop(area_id, None)
|
||||||
|
|
||||||
|
def async_get_sensor_groups(self):
|
||||||
|
"""Fetch a list of sensor groups (websocket API hook)."""
|
||||||
|
groups = self.store.async_get_sensor_groups()
|
||||||
|
return list(groups.values())
|
||||||
|
|
||||||
|
def async_get_group_for_sensor(self, entity_id: str):
|
||||||
|
"""Fetch the group ID for a given sensor."""
|
||||||
|
groups = self.async_get_sensor_groups()
|
||||||
|
result = next((el for el in groups if entity_id in el[ATTR_ENTITIES]), None)
|
||||||
|
return result["group_id"] if result else None
|
||||||
|
|
||||||
|
def assign_sensor_to_group(self, entity_id: str, group_id: str):
|
||||||
|
"""Assign a sensor to a group."""
|
||||||
|
updated = False
|
||||||
|
old_group = self.async_get_group_for_sensor(entity_id)
|
||||||
|
if old_group and group_id != old_group:
|
||||||
|
# remove sensor from group
|
||||||
|
el = self.store.async_get_sensor_group(old_group)
|
||||||
|
if len(el[ATTR_ENTITIES]) > 2:
|
||||||
|
self.store.async_update_sensor_group(
|
||||||
|
old_group,
|
||||||
|
{ATTR_ENTITIES: [x for x in el[ATTR_ENTITIES] if x != entity_id]},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.store.async_delete_sensor_group(old_group)
|
||||||
|
updated = True
|
||||||
|
if group_id:
|
||||||
|
# add sensor to group
|
||||||
|
group = self.store.async_get_sensor_group(group_id)
|
||||||
|
if not group:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Failed to assign entity %s to group %s",
|
||||||
|
entity_id,
|
||||||
|
group_id,
|
||||||
|
)
|
||||||
|
elif entity_id not in group[ATTR_ENTITIES]:
|
||||||
|
self.store.async_update_sensor_group(
|
||||||
|
group_id, {ATTR_ENTITIES: group[ATTR_ENTITIES] + [entity_id]}
|
||||||
|
)
|
||||||
|
updated = True
|
||||||
|
if updated:
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_sensors_updated")
|
||||||
|
|
||||||
|
def async_update_sensor_group_config(
|
||||||
|
self,
|
||||||
|
group_id: str | None = None,
|
||||||
|
data: dict = {},
|
||||||
|
):
|
||||||
|
"""Update sensor group configuration."""
|
||||||
|
if const.ATTR_REMOVE in data:
|
||||||
|
self.store.async_delete_sensor_group(group_id)
|
||||||
|
elif not group_id:
|
||||||
|
self.store.async_create_sensor_group(data)
|
||||||
|
else:
|
||||||
|
self.store.async_update_sensor_group(group_id, data)
|
||||||
|
|
||||||
|
async_dispatcher_send(self.hass, "alarmo_sensors_updated")
|
||||||
|
|
||||||
|
async def async_unload(self):
|
||||||
|
"""Remove all alarmo objects."""
|
||||||
|
# remove alarm_control_panel entities
|
||||||
|
areas = list(self.hass.data[const.DOMAIN]["areas"].keys())
|
||||||
|
for area in areas:
|
||||||
|
await self.async_remove_entity(area)
|
||||||
|
if self.hass.data[const.DOMAIN]["master"]:
|
||||||
|
await self.async_remove_entity("master")
|
||||||
|
|
||||||
|
del self.hass.data[const.DOMAIN]["sensor_handler"]
|
||||||
|
del self.hass.data[const.DOMAIN]["automation_handler"]
|
||||||
|
del self.hass.data[const.DOMAIN]["mqtt_handler"]
|
||||||
|
del self.hass.data[const.DOMAIN]["event_handler"]
|
||||||
|
|
||||||
|
# remove subscriptions for coordinator
|
||||||
|
while len(self._subscriptions):
|
||||||
|
self._subscriptions.pop()()
|
||||||
|
|
||||||
|
async def async_delete_config(self):
|
||||||
|
"""Wipe alarmo storage."""
|
||||||
|
await self.store.async_delete()
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def register_services(hass):
|
||||||
|
"""Register services used by alarmo component."""
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
|
||||||
|
async def async_srv_toggle_user(call):
|
||||||
|
"""Enable a user by service call."""
|
||||||
|
name = call.data.get(ATTR_NAME)
|
||||||
|
enable = True if call.service == const.SERVICE_ENABLE_USER else False
|
||||||
|
users = coordinator.store.async_get_users()
|
||||||
|
user = next(
|
||||||
|
(item for item in list(users.values()) if item[ATTR_NAME] == name), None
|
||||||
|
)
|
||||||
|
if user is None:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Failed to %s user, no match for name '%s'",
|
||||||
|
"enable" if enable else "disable",
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
coordinator.store.async_update_user(
|
||||||
|
user[const.ATTR_USER_ID], {const.ATTR_ENABLED: enable}
|
||||||
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"User user '%s' was %s", name, "enabled" if enable else "disabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
async_register_admin_service(
|
||||||
|
hass,
|
||||||
|
const.DOMAIN,
|
||||||
|
const.SERVICE_ENABLE_USER,
|
||||||
|
async_srv_toggle_user,
|
||||||
|
schema=const.SERVICE_TOGGLE_USER_SCHEMA,
|
||||||
|
)
|
||||||
|
async_register_admin_service(
|
||||||
|
hass,
|
||||||
|
const.DOMAIN,
|
||||||
|
const.SERVICE_DISABLE_USER,
|
||||||
|
async_srv_toggle_user,
|
||||||
|
schema=const.SERVICE_TOGGLE_USER_SCHEMA,
|
||||||
|
)
|
||||||
BIN
custom_components/alarmo/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/automations.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/automations.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/card.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/card.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/config_flow.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/config_flow.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/const.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/const.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/event.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/event.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/helpers.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/helpers.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/mqtt.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/mqtt.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/panel.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/panel.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/sensors.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/sensors.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/store.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/store.cpython-313.pyc
Normal file
Binary file not shown.
BIN
custom_components/alarmo/__pycache__/websockets.cpython-313.pyc
Normal file
BIN
custom_components/alarmo/__pycache__/websockets.cpython-313.pyc
Normal file
Binary file not shown.
1531
custom_components/alarmo/alarm_control_panel.py
Normal file
1531
custom_components/alarmo/alarm_control_panel.py
Normal file
File diff suppressed because it is too large
Load Diff
380
custom_components/alarmo/automations.py
Normal file
380
custom_components/alarmo/automations.py
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
"""Automations."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import copy
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_TYPE,
|
||||||
|
ATTR_SERVICE,
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
CONF_SERVICE_DATA,
|
||||||
|
)
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers.template import Template, is_template_string
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
from homeassistant.helpers.translation import async_get_translations
|
||||||
|
from homeassistant.components.binary_sensor.device_condition import (
|
||||||
|
ENTITY_CONDITIONS,
|
||||||
|
)
|
||||||
|
|
||||||
|
from . import const
|
||||||
|
from .helpers import (
|
||||||
|
friendly_name_for_entity_id,
|
||||||
|
)
|
||||||
|
from .sensors import (
|
||||||
|
STATE_OPEN,
|
||||||
|
STATE_CLOSED,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
)
|
||||||
|
from .alarm_control_panel import AlarmoBaseEntity
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
EVENT_ARM_FAILURE = "arm_failure"
|
||||||
|
|
||||||
|
|
||||||
|
def validate_area(trigger, area_id, hass):
|
||||||
|
"""Validate area for trigger."""
|
||||||
|
if const.ATTR_AREA not in trigger:
|
||||||
|
return False
|
||||||
|
elif trigger[const.ATTR_AREA]:
|
||||||
|
return trigger[const.ATTR_AREA] == area_id
|
||||||
|
elif len(hass.data[const.DOMAIN]["areas"]) == 1:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return area_id is None
|
||||||
|
|
||||||
|
|
||||||
|
def validate_modes(trigger, mode):
|
||||||
|
"""Validate modes for trigger."""
|
||||||
|
if const.ATTR_MODES not in trigger:
|
||||||
|
return False
|
||||||
|
elif not trigger[const.ATTR_MODES]:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return mode in trigger[const.ATTR_MODES]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_trigger(trigger, to_state, from_state=None):
|
||||||
|
"""Validate trigger condition."""
|
||||||
|
if const.ATTR_EVENT not in trigger:
|
||||||
|
return False
|
||||||
|
elif trigger[const.ATTR_EVENT] == "untriggered" and from_state == "triggered":
|
||||||
|
return True
|
||||||
|
elif trigger[const.ATTR_EVENT] == to_state:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class AutomationHandler:
|
||||||
|
"""Handle automations."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant):
|
||||||
|
"""Initialize automation handler."""
|
||||||
|
self.hass = hass
|
||||||
|
self._config = None
|
||||||
|
self._subscriptions = []
|
||||||
|
self._sensorTranslationCache = {}
|
||||||
|
self._alarmTranslationCache = {}
|
||||||
|
self._sensorTranslationLang = None
|
||||||
|
self._alarmTranslationLang = None
|
||||||
|
|
||||||
|
def async_update_config():
|
||||||
|
"""Automation config updated, reload the configuration."""
|
||||||
|
self._config = self.hass.data[const.DOMAIN][
|
||||||
|
"coordinator"
|
||||||
|
].store.async_get_automations()
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
hass, "alarmo_automations_updated", async_update_config
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async_update_config()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
async def async_alarm_state_changed(
|
||||||
|
area_id: str, old_state: str, new_state: str
|
||||||
|
):
|
||||||
|
if not old_state:
|
||||||
|
# ignore automations at startup/restoring
|
||||||
|
return
|
||||||
|
|
||||||
|
if area_id:
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
else:
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["master"]
|
||||||
|
|
||||||
|
if not alarm_entity:
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"state of %s is updated from %s to %s",
|
||||||
|
alarm_entity.entity_id,
|
||||||
|
old_state,
|
||||||
|
new_state,
|
||||||
|
)
|
||||||
|
|
||||||
|
if new_state in const.ARM_MODES:
|
||||||
|
# we don't distinguish between armed modes for automations
|
||||||
|
# they are handled separately
|
||||||
|
new_state = "armed"
|
||||||
|
|
||||||
|
for automation_id, config in self._config.items():
|
||||||
|
if not config[const.ATTR_ENABLED]:
|
||||||
|
continue
|
||||||
|
for trigger in config[const.ATTR_TRIGGERS]:
|
||||||
|
if (
|
||||||
|
validate_area(trigger, area_id, self.hass)
|
||||||
|
and validate_modes(trigger, alarm_entity._arm_mode)
|
||||||
|
and validate_trigger(trigger, new_state, old_state)
|
||||||
|
):
|
||||||
|
await self.async_execute_automation(automation_id, alarm_entity)
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
self.hass, "alarmo_state_updated", async_alarm_state_changed
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
async def async_handle_event(event: str, area_id: str, args: dict = {}):
|
||||||
|
if event != const.EVENT_FAILED_TO_ARM:
|
||||||
|
return
|
||||||
|
if area_id:
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
else:
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["master"]
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s has failed to arm",
|
||||||
|
alarm_entity.entity_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
for automation_id, config in self._config.items():
|
||||||
|
if not config[const.ATTR_ENABLED]:
|
||||||
|
continue
|
||||||
|
for trigger in config[const.ATTR_TRIGGERS]:
|
||||||
|
if (
|
||||||
|
validate_area(trigger, area_id, self.hass)
|
||||||
|
and validate_modes(trigger, alarm_entity._arm_mode)
|
||||||
|
and validate_trigger(trigger, EVENT_ARM_FAILURE)
|
||||||
|
):
|
||||||
|
await self.async_execute_automation(automation_id, alarm_entity)
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(self.hass, "alarmo_event", async_handle_event)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Prepare for removal."""
|
||||||
|
while len(self._subscriptions):
|
||||||
|
self._subscriptions.pop()()
|
||||||
|
|
||||||
|
async def async_execute_automation(
|
||||||
|
self, automation_id: str, alarm_entity: AlarmoBaseEntity
|
||||||
|
):
|
||||||
|
"""Execute the specified automation."""
|
||||||
|
# automation is a dict of AutomationEntry
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Executing automation %s",
|
||||||
|
automation_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
actions = self._config[automation_id][const.ATTR_ACTIONS]
|
||||||
|
for action in actions:
|
||||||
|
try:
|
||||||
|
service_data = copy.copy(action[CONF_SERVICE_DATA])
|
||||||
|
|
||||||
|
if action.get(ATTR_ENTITY_ID):
|
||||||
|
service_data[ATTR_ENTITY_ID] = action[ATTR_ENTITY_ID]
|
||||||
|
|
||||||
|
if self._config[automation_id][CONF_TYPE] == const.ATTR_NOTIFICATION:
|
||||||
|
# replace wildcards within service_data struct
|
||||||
|
for key, val in service_data.items():
|
||||||
|
if type(val) is str:
|
||||||
|
service_data[key] = await self.replace_wildcards_in_string(
|
||||||
|
val, alarm_entity
|
||||||
|
)
|
||||||
|
elif type(val) is dict:
|
||||||
|
for subkey, subval in service_data[key].items():
|
||||||
|
if type(subval) is str:
|
||||||
|
service_data[key][
|
||||||
|
subkey
|
||||||
|
] = await self.replace_wildcards_in_string(
|
||||||
|
subval, alarm_entity
|
||||||
|
)
|
||||||
|
|
||||||
|
domain, service = action[ATTR_SERVICE].split(".")
|
||||||
|
|
||||||
|
await self.hass.async_create_task(
|
||||||
|
self.hass.services.async_call(
|
||||||
|
domain,
|
||||||
|
service,
|
||||||
|
service_data,
|
||||||
|
blocking=False,
|
||||||
|
context={},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except HomeAssistantError as e:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Execution of action %s failed, reason: %s",
|
||||||
|
automation_id,
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_automations_by_area(self, area_id: str):
|
||||||
|
"""Get automations for specified area."""
|
||||||
|
result = []
|
||||||
|
for automation_id, config in self._config.items():
|
||||||
|
if any(
|
||||||
|
el[const.ATTR_AREA] == area_id for el in config[const.ATTR_TRIGGERS]
|
||||||
|
):
|
||||||
|
result.append(automation_id)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def replace_wildcards_in_string(
|
||||||
|
self, input: str, alarm_entity: AlarmoBaseEntity
|
||||||
|
):
|
||||||
|
"""Look for wildcards in string and replace them with content."""
|
||||||
|
# process wildcard '{{open_sensors}}'
|
||||||
|
res = re.search(r"{{open_sensors(\|lang=([^}]+))?(\|format=short)?}}", input)
|
||||||
|
if res:
|
||||||
|
lang = res.group(2) if res.group(2) else "en"
|
||||||
|
names_only = True if res.group(3) else False
|
||||||
|
|
||||||
|
open_sensors = ""
|
||||||
|
if alarm_entity.open_sensors:
|
||||||
|
parts = []
|
||||||
|
for entity_id, status in alarm_entity.open_sensors.items():
|
||||||
|
if names_only:
|
||||||
|
parts.append(friendly_name_for_entity_id(entity_id, self.hass))
|
||||||
|
else:
|
||||||
|
parts.append(
|
||||||
|
await self.async_get_open_sensor_string(
|
||||||
|
entity_id, status, lang
|
||||||
|
)
|
||||||
|
)
|
||||||
|
open_sensors = ", ".join(parts)
|
||||||
|
input = input.replace(res.group(0), open_sensors)
|
||||||
|
|
||||||
|
# process wildcard '{{bypassed_sensors}}'
|
||||||
|
if "{{bypassed_sensors}}" in input:
|
||||||
|
bypassed_sensors = ""
|
||||||
|
if alarm_entity.bypassed_sensors and len(alarm_entity.bypassed_sensors):
|
||||||
|
parts = []
|
||||||
|
for entity_id in alarm_entity.bypassed_sensors:
|
||||||
|
name = friendly_name_for_entity_id(entity_id, self.hass)
|
||||||
|
parts.append(name)
|
||||||
|
bypassed_sensors = ", ".join(parts)
|
||||||
|
input = input.replace("{{bypassed_sensors}}", bypassed_sensors)
|
||||||
|
|
||||||
|
# process wildcard '{{arm_mode}}'
|
||||||
|
res = re.search(r"{{arm_mode(\|lang=([^}]+))?}}", input)
|
||||||
|
if res:
|
||||||
|
lang = res.group(2) if res.group(2) else "en"
|
||||||
|
arm_mode = await self.async_get_arm_mode_string(alarm_entity.arm_mode, lang)
|
||||||
|
|
||||||
|
input = input.replace(res.group(0), arm_mode)
|
||||||
|
|
||||||
|
# process wildcard '{{changed_by}}'
|
||||||
|
if "{{changed_by}}" in input:
|
||||||
|
changed_by = alarm_entity.changed_by if alarm_entity.changed_by else ""
|
||||||
|
input = input.replace("{{changed_by}}", changed_by)
|
||||||
|
|
||||||
|
# process wildcard '{{delay}}'
|
||||||
|
if "{{delay}}" in input:
|
||||||
|
delay = str(alarm_entity.delay) if alarm_entity.delay else ""
|
||||||
|
input = input.replace("{{delay}}", delay)
|
||||||
|
|
||||||
|
# process HA templates
|
||||||
|
if is_template_string(input):
|
||||||
|
input = Template(input, self.hass).async_render()
|
||||||
|
|
||||||
|
return input
|
||||||
|
|
||||||
|
async def async_get_open_sensor_string(
|
||||||
|
self, entity_id: str, state: str, language: str
|
||||||
|
):
|
||||||
|
"""Get translation for sensor states."""
|
||||||
|
if self._sensorTranslationCache and self._sensorTranslationLang == language:
|
||||||
|
translations = self._sensorTranslationCache
|
||||||
|
else:
|
||||||
|
translations = await async_get_translations(
|
||||||
|
self.hass, language, "device_automation", ["binary_sensor"]
|
||||||
|
)
|
||||||
|
|
||||||
|
self._sensorTranslationCache = translations
|
||||||
|
self._sensorTranslationLang = language
|
||||||
|
|
||||||
|
entity = self.hass.states.get(entity_id)
|
||||||
|
|
||||||
|
device_type = (
|
||||||
|
entity.attributes["device_class"]
|
||||||
|
if entity and "device_class" in entity.attributes
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
if state == STATE_OPEN:
|
||||||
|
translation_key = (
|
||||||
|
f"component.binary_sensor.device_automation.condition_type.{ENTITY_CONDITIONS[device_type][0]['type']}"
|
||||||
|
if device_type in ENTITY_CONDITIONS
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
if translation_key and translation_key in translations:
|
||||||
|
string = translations[translation_key]
|
||||||
|
else:
|
||||||
|
string = "{entity_name} is open"
|
||||||
|
elif state == STATE_CLOSED:
|
||||||
|
translation_key = (
|
||||||
|
f"component.binary_sensor.device_automation.condition_type.{ENTITY_CONDITIONS[device_type][1]['type']}"
|
||||||
|
if device_type in ENTITY_CONDITIONS
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
if translation_key and translation_key in translations:
|
||||||
|
string = translations[translation_key]
|
||||||
|
else:
|
||||||
|
string = "{entity_name} is closed"
|
||||||
|
|
||||||
|
elif state == STATE_UNAVAILABLE:
|
||||||
|
string = "{entity_name} is unavailable"
|
||||||
|
|
||||||
|
else:
|
||||||
|
string = "{entity_name} is unknown"
|
||||||
|
|
||||||
|
name = friendly_name_for_entity_id(entity_id, self.hass)
|
||||||
|
string = string.replace("{entity_name}", name)
|
||||||
|
|
||||||
|
return string
|
||||||
|
|
||||||
|
async def async_get_arm_mode_string(self, arm_mode: str, language: str):
|
||||||
|
"""Get translation for alarm arm mode."""
|
||||||
|
if self._alarmTranslationCache and self._alarmTranslationLang == language:
|
||||||
|
translations = self._alarmTranslationCache
|
||||||
|
else:
|
||||||
|
translations = await async_get_translations(
|
||||||
|
self.hass, language, "entity_component", ["alarm_control_panel"]
|
||||||
|
)
|
||||||
|
|
||||||
|
self._alarmTranslationCache = translations
|
||||||
|
self._alarmTranslationLang = language
|
||||||
|
|
||||||
|
translation_key = (
|
||||||
|
f"component.alarm_control_panel.entity_component._.state.{arm_mode}"
|
||||||
|
if arm_mode
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
if translation_key and translation_key in translations:
|
||||||
|
return translations[translation_key]
|
||||||
|
elif arm_mode:
|
||||||
|
return " ".join(w.capitalize() for w in arm_mode.split("_"))
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
34
custom_components/alarmo/card.py
Normal file
34
custom_components/alarmo/card.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
"""WebSocket handler and registration for Alarmo card update events."""
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
from homeassistant.components.websocket_api import decorators, async_register_command
|
||||||
|
|
||||||
|
|
||||||
|
@decorators.websocket_command(
|
||||||
|
{
|
||||||
|
vol.Required("type"): "alarmo_updated",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@decorators.async_response
|
||||||
|
async def handle_subscribe_updates(hass, connection, msg):
|
||||||
|
"""Handle subscribe updates."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def handle_event(event: str, area_id: str, args: dict = {}):
|
||||||
|
"""Forward events to websocket."""
|
||||||
|
data = dict(**args, **{"event": event, "area_id": area_id})
|
||||||
|
connection.send_message(
|
||||||
|
{"id": msg["id"], "type": "event", "event": {"data": data}}
|
||||||
|
)
|
||||||
|
|
||||||
|
connection.subscriptions[msg["id"]] = async_dispatcher_connect(
|
||||||
|
hass, "alarmo_event", handle_event
|
||||||
|
)
|
||||||
|
connection.send_result(msg["id"])
|
||||||
|
|
||||||
|
|
||||||
|
async def async_register_card(hass):
|
||||||
|
"""Publish event to lovelace when alarm changes."""
|
||||||
|
async_register_command(hass, handle_subscribe_updates)
|
||||||
30
custom_components/alarmo/config_flow.py
Normal file
30
custom_components/alarmo/config_flow.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
"""Config flow for the Alarmo component."""
|
||||||
|
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
NAME,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Config flow for Alarmo."""
|
||||||
|
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Handle a flow initialized by the user."""
|
||||||
|
# Only a single instance of the integration
|
||||||
|
if self._async_current_entries():
|
||||||
|
return self.async_abort(reason="single_instance_allowed")
|
||||||
|
|
||||||
|
id = secrets.token_hex(6)
|
||||||
|
|
||||||
|
await self.async_set_unique_id(id)
|
||||||
|
self._abort_if_unique_id_configured(updates=user_input)
|
||||||
|
|
||||||
|
return self.async_create_entry(title=NAME, data={})
|
||||||
234
custom_components/alarmo/const.py
Normal file
234
custom_components/alarmo/const.py
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
"""Store constants."""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_NAME,
|
||||||
|
CONF_CODE,
|
||||||
|
CONF_MODE,
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.components.alarm_control_panel import (
|
||||||
|
AlarmControlPanelState,
|
||||||
|
AlarmControlPanelEntityFeature,
|
||||||
|
)
|
||||||
|
|
||||||
|
VERSION = "1.10.13"
|
||||||
|
NAME = "Alarmo"
|
||||||
|
MANUFACTURER = "@nielsfaber"
|
||||||
|
|
||||||
|
DOMAIN = "alarmo"
|
||||||
|
|
||||||
|
CUSTOM_COMPONENTS = "custom_components"
|
||||||
|
INTEGRATION_FOLDER = DOMAIN
|
||||||
|
PANEL_FOLDER = "frontend"
|
||||||
|
PANEL_FILENAME = "dist/alarm-panel.js"
|
||||||
|
|
||||||
|
PANEL_URL = "/api/panel_custom/alarmo"
|
||||||
|
PANEL_TITLE = NAME
|
||||||
|
PANEL_ICON = "mdi:shield-home"
|
||||||
|
PANEL_NAME = "alarm-panel"
|
||||||
|
|
||||||
|
INITIALIZATION_TIME = datetime.timedelta(seconds=60)
|
||||||
|
SENSOR_ARM_TIME = datetime.timedelta(seconds=5)
|
||||||
|
|
||||||
|
STATES = [
|
||||||
|
AlarmControlPanelState.ARMED_AWAY,
|
||||||
|
AlarmControlPanelState.ARMED_HOME,
|
||||||
|
AlarmControlPanelState.ARMED_NIGHT,
|
||||||
|
AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
|
||||||
|
AlarmControlPanelState.ARMED_VACATION,
|
||||||
|
AlarmControlPanelState.DISARMED,
|
||||||
|
AlarmControlPanelState.TRIGGERED,
|
||||||
|
AlarmControlPanelState.PENDING,
|
||||||
|
AlarmControlPanelState.ARMING,
|
||||||
|
]
|
||||||
|
|
||||||
|
ARM_MODES = [
|
||||||
|
AlarmControlPanelState.ARMED_AWAY,
|
||||||
|
AlarmControlPanelState.ARMED_HOME,
|
||||||
|
AlarmControlPanelState.ARMED_NIGHT,
|
||||||
|
AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
|
||||||
|
AlarmControlPanelState.ARMED_VACATION,
|
||||||
|
]
|
||||||
|
|
||||||
|
ARM_MODE_TO_STATE = {
|
||||||
|
"away": AlarmControlPanelState.ARMED_AWAY,
|
||||||
|
"home": AlarmControlPanelState.ARMED_HOME,
|
||||||
|
"night": AlarmControlPanelState.ARMED_NIGHT,
|
||||||
|
"custom": AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
|
||||||
|
"vacation": AlarmControlPanelState.ARMED_VACATION,
|
||||||
|
}
|
||||||
|
|
||||||
|
STATE_TO_ARM_MODE = {
|
||||||
|
AlarmControlPanelState.ARMED_AWAY: "away",
|
||||||
|
AlarmControlPanelState.ARMED_HOME: "home",
|
||||||
|
AlarmControlPanelState.ARMED_NIGHT: "night",
|
||||||
|
AlarmControlPanelState.ARMED_CUSTOM_BYPASS: "custom",
|
||||||
|
AlarmControlPanelState.ARMED_VACATION: "vacation",
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_ARM_NIGHT = "arm_night"
|
||||||
|
COMMAND_ARM_AWAY = "arm_away"
|
||||||
|
COMMAND_ARM_HOME = "arm_home"
|
||||||
|
COMMAND_ARM_CUSTOM_BYPASS = "arm_custom_bypass"
|
||||||
|
COMMAND_ARM_VACATION = "arm_vacation"
|
||||||
|
COMMAND_DISARM = "disarm"
|
||||||
|
|
||||||
|
COMMANDS = [
|
||||||
|
COMMAND_DISARM,
|
||||||
|
COMMAND_ARM_AWAY,
|
||||||
|
COMMAND_ARM_NIGHT,
|
||||||
|
COMMAND_ARM_HOME,
|
||||||
|
COMMAND_ARM_CUSTOM_BYPASS,
|
||||||
|
COMMAND_ARM_VACATION,
|
||||||
|
]
|
||||||
|
|
||||||
|
EVENT_DISARM = "disarm"
|
||||||
|
EVENT_LEAVE = "leave"
|
||||||
|
EVENT_ARM = "arm"
|
||||||
|
EVENT_ENTRY = "entry"
|
||||||
|
EVENT_TRIGGER = "trigger"
|
||||||
|
EVENT_FAILED_TO_ARM = "failed_to_arm"
|
||||||
|
EVENT_COMMAND_NOT_ALLOWED = "command_not_allowed"
|
||||||
|
EVENT_INVALID_CODE_PROVIDED = "invalid_code_provided"
|
||||||
|
EVENT_NO_CODE_PROVIDED = "no_code_provided"
|
||||||
|
EVENT_TRIGGER_TIME_EXPIRED = "trigger_time_expired"
|
||||||
|
EVENT_READY_TO_ARM_MODES_CHANGED = "ready_to_arm_modes_changed"
|
||||||
|
|
||||||
|
ATTR_MODES = "modes"
|
||||||
|
ATTR_ARM_MODE = "arm_mode"
|
||||||
|
ATTR_CODE_DISARM_REQUIRED = "code_disarm_required"
|
||||||
|
ATTR_CODE_MODE_CHANGE_REQUIRED = "code_mode_change_required"
|
||||||
|
ATTR_REMOVE = "remove"
|
||||||
|
ATTR_OLD_CODE = "old_code"
|
||||||
|
|
||||||
|
ATTR_TRIGGER_TIME = "trigger_time"
|
||||||
|
ATTR_EXIT_TIME = "exit_time"
|
||||||
|
ATTR_ENTRY_TIME = "entry_time"
|
||||||
|
|
||||||
|
ATTR_ENABLED = "enabled"
|
||||||
|
ATTR_USER_ID = "user_id"
|
||||||
|
|
||||||
|
ATTR_CAN_ARM = "can_arm"
|
||||||
|
ATTR_CAN_DISARM = "can_disarm"
|
||||||
|
ATTR_DISARM_AFTER_TRIGGER = "disarm_after_trigger"
|
||||||
|
ATTR_IGNORE_BLOCKING_SENSORS_AFTER_TRIGGER = "ignore_blocking_sensors_after_trigger"
|
||||||
|
|
||||||
|
ATTR_REMOVE = "remove"
|
||||||
|
ATTR_IS_OVERRIDE_CODE = "is_override_code"
|
||||||
|
ATTR_AREA_LIMIT = "area_limit"
|
||||||
|
ATTR_CODE_FORMAT = "code_format"
|
||||||
|
ATTR_CODE_LENGTH = "code_length"
|
||||||
|
|
||||||
|
ATTR_AUTOMATION_ID = "automation_id"
|
||||||
|
|
||||||
|
ATTR_TYPE = "type"
|
||||||
|
ATTR_AREA = "area"
|
||||||
|
ATTR_MASTER = "master"
|
||||||
|
|
||||||
|
ATTR_TRIGGERS = "triggers"
|
||||||
|
ATTR_ACTIONS = "actions"
|
||||||
|
ATTR_EVENT = "event"
|
||||||
|
ATTR_REQUIRE_CODE = "require_code"
|
||||||
|
|
||||||
|
ATTR_NOTIFICATION = "notification"
|
||||||
|
ATTR_VERSION = "version"
|
||||||
|
ATTR_STATE_PAYLOAD = "state_payload"
|
||||||
|
ATTR_COMMAND_PAYLOAD = "command_payload"
|
||||||
|
|
||||||
|
ATTR_FORCE = "force"
|
||||||
|
ATTR_SKIP_DELAY = "skip_delay"
|
||||||
|
ATTR_CONTEXT_ID = "context_id"
|
||||||
|
|
||||||
|
PUSH_EVENT = "mobile_app_notification_action"
|
||||||
|
|
||||||
|
EVENT_ACTION_FORCE_ARM = "ALARMO_FORCE_ARM"
|
||||||
|
EVENT_ACTION_RETRY_ARM = "ALARMO_RETRY_ARM"
|
||||||
|
EVENT_ACTION_DISARM = "ALARMO_DISARM"
|
||||||
|
EVENT_ACTION_ARM_AWAY = "ALARMO_ARM_AWAY"
|
||||||
|
EVENT_ACTION_ARM_HOME = "ALARMO_ARM_HOME"
|
||||||
|
EVENT_ACTION_ARM_NIGHT = "ALARMO_ARM_NIGHT"
|
||||||
|
EVENT_ACTION_ARM_VACATION = "ALARMO_ARM_VACATION"
|
||||||
|
EVENT_ACTION_ARM_CUSTOM_BYPASS = "ALARMO_ARM_CUSTOM_BYPASS"
|
||||||
|
|
||||||
|
EVENT_ACTIONS = [
|
||||||
|
EVENT_ACTION_FORCE_ARM,
|
||||||
|
EVENT_ACTION_RETRY_ARM,
|
||||||
|
EVENT_ACTION_DISARM,
|
||||||
|
EVENT_ACTION_ARM_AWAY,
|
||||||
|
EVENT_ACTION_ARM_HOME,
|
||||||
|
EVENT_ACTION_ARM_NIGHT,
|
||||||
|
EVENT_ACTION_ARM_VACATION,
|
||||||
|
EVENT_ACTION_ARM_CUSTOM_BYPASS,
|
||||||
|
]
|
||||||
|
|
||||||
|
MODES_TO_SUPPORTED_FEATURES = {
|
||||||
|
AlarmControlPanelState.ARMED_AWAY: AlarmControlPanelEntityFeature.ARM_AWAY,
|
||||||
|
AlarmControlPanelState.ARMED_HOME: AlarmControlPanelEntityFeature.ARM_HOME,
|
||||||
|
AlarmControlPanelState.ARMED_NIGHT: AlarmControlPanelEntityFeature.ARM_NIGHT,
|
||||||
|
AlarmControlPanelState.ARMED_CUSTOM_BYPASS: AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS, # noqa: E501
|
||||||
|
AlarmControlPanelState.ARMED_VACATION: AlarmControlPanelEntityFeature.ARM_VACATION,
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVICE_ARM = "arm"
|
||||||
|
SERVICE_DISARM = "disarm"
|
||||||
|
SERVICE_SKIP_DELAY = "skip_delay"
|
||||||
|
|
||||||
|
CONF_ALARM_ARMED_AWAY = "armed_away"
|
||||||
|
CONF_ALARM_ARMED_CUSTOM_BYPASS = "armed_custom_bypass"
|
||||||
|
CONF_ALARM_ARMED_HOME = "armed_home"
|
||||||
|
CONF_ALARM_ARMED_NIGHT = "armed_night"
|
||||||
|
CONF_ALARM_ARMED_VACATION = "armed_vacation"
|
||||||
|
CONF_ALARM_ARMING = "arming"
|
||||||
|
CONF_ALARM_DISARMED = "disarmed"
|
||||||
|
CONF_ALARM_PENDING = "pending"
|
||||||
|
CONF_ALARM_TRIGGERED = "triggered"
|
||||||
|
|
||||||
|
SERVICE_ARM_SCHEMA = cv.make_entity_service_schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
|
||||||
|
vol.Optional(CONF_CODE, default=""): cv.string,
|
||||||
|
vol.Optional(CONF_MODE, default=AlarmControlPanelState.ARMED_AWAY): vol.In(
|
||||||
|
[
|
||||||
|
"away",
|
||||||
|
"home",
|
||||||
|
"night",
|
||||||
|
"custom",
|
||||||
|
"vacation",
|
||||||
|
CONF_ALARM_ARMED_AWAY,
|
||||||
|
CONF_ALARM_ARMED_HOME,
|
||||||
|
CONF_ALARM_ARMED_NIGHT,
|
||||||
|
CONF_ALARM_ARMED_CUSTOM_BYPASS,
|
||||||
|
CONF_ALARM_ARMED_VACATION,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
vol.Optional(ATTR_SKIP_DELAY, default=False): cv.boolean,
|
||||||
|
vol.Optional(ATTR_FORCE, default=False): cv.boolean,
|
||||||
|
vol.Optional(ATTR_CONTEXT_ID): int,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
SERVICE_DISARM_SCHEMA = cv.make_entity_service_schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
|
||||||
|
vol.Optional(CONF_CODE, default=""): cv.string,
|
||||||
|
vol.Optional(ATTR_CONTEXT_ID): int,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
SERVICE_SKIP_DELAY_SCHEMA = cv.make_entity_service_schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
SERVICE_ENABLE_USER = "enable_user"
|
||||||
|
SERVICE_DISABLE_USER = "disable_user"
|
||||||
|
SERVICE_TOGGLE_USER_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_NAME, default=""): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
89
custom_components/alarmo/event.py
Normal file
89
custom_components/alarmo/event.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
"""fire events in HA for use with automations."""
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
|
from . import const
|
||||||
|
|
||||||
|
|
||||||
|
class EventHandler:
|
||||||
|
"""Class to handle events from Alarmo and fire HA events."""
|
||||||
|
|
||||||
|
def __init__(self, hass):
|
||||||
|
"""Class constructor."""
|
||||||
|
self.hass = hass
|
||||||
|
self._subscription = async_dispatcher_connect(
|
||||||
|
self.hass, "alarmo_event", self.async_handle_event
|
||||||
|
)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Class destructor."""
|
||||||
|
self._subscription()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_handle_event(self, event: str, area_id: str, args: dict = {}):
|
||||||
|
"""Handle event."""
|
||||||
|
if area_id:
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
else:
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["master"]
|
||||||
|
|
||||||
|
if event in [
|
||||||
|
const.EVENT_FAILED_TO_ARM,
|
||||||
|
const.EVENT_COMMAND_NOT_ALLOWED,
|
||||||
|
const.EVENT_INVALID_CODE_PROVIDED,
|
||||||
|
const.EVENT_NO_CODE_PROVIDED,
|
||||||
|
]:
|
||||||
|
reasons = {
|
||||||
|
const.EVENT_FAILED_TO_ARM: "open_sensors",
|
||||||
|
const.EVENT_COMMAND_NOT_ALLOWED: "not_allowed",
|
||||||
|
const.EVENT_INVALID_CODE_PROVIDED: "invalid_code",
|
||||||
|
const.EVENT_NO_CODE_PROVIDED: "invalid_code",
|
||||||
|
}
|
||||||
|
|
||||||
|
data = dict(
|
||||||
|
**args,
|
||||||
|
**{
|
||||||
|
"area_id": area_id,
|
||||||
|
"entity_id": alarm_entity.entity_id,
|
||||||
|
"reason": reasons[event],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if "open_sensors" in data:
|
||||||
|
data["sensors"] = list(data["open_sensors"].keys())
|
||||||
|
del data["open_sensors"]
|
||||||
|
|
||||||
|
self.hass.bus.async_fire("alarmo_failed_to_arm", data)
|
||||||
|
|
||||||
|
elif event in [const.EVENT_ARM, const.EVENT_DISARM]:
|
||||||
|
data = dict(
|
||||||
|
**args,
|
||||||
|
**{
|
||||||
|
"area_id": area_id,
|
||||||
|
"entity_id": alarm_entity.entity_id,
|
||||||
|
"action": event,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if "arm_mode" in data:
|
||||||
|
data["mode"] = const.STATE_TO_ARM_MODE[data["arm_mode"]]
|
||||||
|
del data["arm_mode"]
|
||||||
|
|
||||||
|
self.hass.bus.async_fire("alarmo_command_success", data)
|
||||||
|
|
||||||
|
elif event == const.EVENT_READY_TO_ARM_MODES_CHANGED:
|
||||||
|
supported_modes = dict(
|
||||||
|
filter(
|
||||||
|
lambda el: el[1] & alarm_entity.supported_features,
|
||||||
|
const.MODES_TO_SUPPORTED_FEATURES.items(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
modes = {
|
||||||
|
k.value: (k.value in args["modes"]) for k in supported_modes.keys()
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
"area_id": area_id,
|
||||||
|
"entity_id": alarm_entity.entity_id,
|
||||||
|
**modes,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.hass.bus.async_fire("alarmo_ready_to_arm_modes_updated", data)
|
||||||
3288
custom_components/alarmo/frontend/dist/alarm-panel.js
vendored
Normal file
3288
custom_components/alarmo/frontend/dist/alarm-panel.js
vendored
Normal file
File diff suppressed because one or more lines are too long
19
custom_components/alarmo/helpers.py
Normal file
19
custom_components/alarmo/helpers.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
"""Helper functions for Alarmo integration."""
|
||||||
|
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def friendly_name_for_entity_id(entity_id: str, hass: HomeAssistant):
|
||||||
|
"""Helper to get friendly name for entity."""
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
if state and state.attributes.get("friendly_name"):
|
||||||
|
return state.attributes["friendly_name"]
|
||||||
|
|
||||||
|
return entity_id
|
||||||
|
|
||||||
|
|
||||||
|
def omit(obj: dict, blacklisted_keys: list):
|
||||||
|
"""Helper to omit blacklisted keys from a dict."""
|
||||||
|
return {key: val for key, val in obj.items() if key not in blacklisted_keys}
|
||||||
8
custom_components/alarmo/icons.json
Normal file
8
custom_components/alarmo/icons.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"services": {
|
||||||
|
"arm": "mdi:shield-lock",
|
||||||
|
"disarm": "mdi:shield-off",
|
||||||
|
"enable_user": "mdi:account-lock-open",
|
||||||
|
"disable_user": "mdi:account-lock-closed"
|
||||||
|
}
|
||||||
|
}
|
||||||
21
custom_components/alarmo/manifest.json
Normal file
21
custom_components/alarmo/manifest.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"domain": "alarmo",
|
||||||
|
"name": "Alarmo",
|
||||||
|
"after_dependencies": [
|
||||||
|
"mqtt",
|
||||||
|
"notify"
|
||||||
|
],
|
||||||
|
"codeowners": [
|
||||||
|
"@nielsfaber"
|
||||||
|
],
|
||||||
|
"config_flow": true,
|
||||||
|
"dependencies": [
|
||||||
|
"http",
|
||||||
|
"panel_custom"
|
||||||
|
],
|
||||||
|
"documentation": "https://github.com/nielsfaber/alarmo",
|
||||||
|
"iot_class": "local_push",
|
||||||
|
"issue_tracker": "https://github.com/nielsfaber/alarmo/issues",
|
||||||
|
"requirements": [],
|
||||||
|
"version": "1.10.13"
|
||||||
|
}
|
||||||
319
custom_components/alarmo/mqtt.py
Normal file
319
custom_components/alarmo/mqtt.py
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
"""Class to handle MQTT integration."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
from homeassistant.util import slugify
|
||||||
|
from homeassistant.components import mqtt
|
||||||
|
from homeassistant.helpers.json import JSONEncoder
|
||||||
|
from homeassistant.components.mqtt import (
|
||||||
|
DOMAIN as ATTR_MQTT,
|
||||||
|
)
|
||||||
|
from homeassistant.components.mqtt import (
|
||||||
|
CONF_STATE_TOPIC,
|
||||||
|
CONF_COMMAND_TOPIC,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
|
from . import const
|
||||||
|
from .helpers import (
|
||||||
|
friendly_name_for_entity_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
CONF_EVENT_TOPIC = "event_topic"
|
||||||
|
|
||||||
|
|
||||||
|
class MqttHandler:
|
||||||
|
"""Class to handle MQTT integration."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant): # noqa: PLR0915
|
||||||
|
"""Class constructor."""
|
||||||
|
self.hass = hass
|
||||||
|
self._config = None
|
||||||
|
self._subscribed_topics = []
|
||||||
|
self._subscriptions = []
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_config(_args=None):
|
||||||
|
"""Mqtt config updated, reload the configuration."""
|
||||||
|
old_config = self._config
|
||||||
|
new_config = self.hass.data[const.DOMAIN][
|
||||||
|
"coordinator"
|
||||||
|
].store.async_get_config()
|
||||||
|
|
||||||
|
if old_config and old_config[ATTR_MQTT] == new_config[ATTR_MQTT]:
|
||||||
|
# only update MQTT config if some parameters are changed
|
||||||
|
return
|
||||||
|
|
||||||
|
self._config = new_config
|
||||||
|
|
||||||
|
if (
|
||||||
|
not old_config
|
||||||
|
or old_config[ATTR_MQTT][CONF_COMMAND_TOPIC]
|
||||||
|
!= new_config[ATTR_MQTT][CONF_COMMAND_TOPIC]
|
||||||
|
):
|
||||||
|
# re-subscribing is only needed if the command topic has changed
|
||||||
|
self.hass.add_job(self._async_subscribe_topics())
|
||||||
|
|
||||||
|
_LOGGER.debug("MQTT config was (re)loaded")
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(hass, "alarmo_config_updated", async_update_config)
|
||||||
|
)
|
||||||
|
async_update_config()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_alarm_state_changed(area_id: str, old_state: str, new_state: str):
|
||||||
|
if not self._config[ATTR_MQTT][const.ATTR_ENABLED]:
|
||||||
|
return
|
||||||
|
|
||||||
|
topic = self._config[ATTR_MQTT][CONF_STATE_TOPIC]
|
||||||
|
|
||||||
|
if not topic: # do not publish if no topic is provided
|
||||||
|
return
|
||||||
|
|
||||||
|
if area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1:
|
||||||
|
# handle the sending of a state update for a specific area
|
||||||
|
area = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
topic = topic.rsplit("/", 1)
|
||||||
|
topic.insert(1, slugify(area.name))
|
||||||
|
topic = "/".join(topic)
|
||||||
|
|
||||||
|
payload_config = self._config[ATTR_MQTT][const.ATTR_STATE_PAYLOAD]
|
||||||
|
if payload_config.get(new_state):
|
||||||
|
message = payload_config[new_state]
|
||||||
|
else:
|
||||||
|
message = new_state
|
||||||
|
|
||||||
|
hass.async_create_task(
|
||||||
|
mqtt.async_publish(self.hass, topic, message, retain=True)
|
||||||
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Published state '%s' on topic '%s'",
|
||||||
|
message,
|
||||||
|
topic,
|
||||||
|
)
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
self.hass, "alarmo_state_updated", async_alarm_state_changed
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_handle_event(event: str, area_id: str, args: dict = {}):
|
||||||
|
if not self._config[ATTR_MQTT][const.ATTR_ENABLED]:
|
||||||
|
return
|
||||||
|
|
||||||
|
topic = self._config[ATTR_MQTT][CONF_EVENT_TOPIC]
|
||||||
|
|
||||||
|
if not topic: # do not publish if no topic is provided
|
||||||
|
return
|
||||||
|
|
||||||
|
if area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1:
|
||||||
|
# handle the sending of a state update for a specific area
|
||||||
|
area = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
topic = topic.rsplit("/", 1)
|
||||||
|
topic.insert(1, slugify(area.name))
|
||||||
|
topic = "/".join(topic)
|
||||||
|
|
||||||
|
if event == const.EVENT_ARM:
|
||||||
|
payload = {
|
||||||
|
"event": f"{event.upper()}_{args['arm_mode'].split('_', 1).pop(1).upper()}", # noqa: E501
|
||||||
|
"delay": args["delay"],
|
||||||
|
}
|
||||||
|
elif event == const.EVENT_TRIGGER:
|
||||||
|
payload = {
|
||||||
|
"event": event.upper(),
|
||||||
|
"delay": args["delay"],
|
||||||
|
"sensors": [
|
||||||
|
{
|
||||||
|
"entity_id": entity,
|
||||||
|
"name": friendly_name_for_entity_id(entity, self.hass),
|
||||||
|
}
|
||||||
|
for (entity, state) in args["open_sensors"].items()
|
||||||
|
],
|
||||||
|
}
|
||||||
|
elif event == const.EVENT_FAILED_TO_ARM:
|
||||||
|
payload = {
|
||||||
|
"event": event.upper(),
|
||||||
|
"sensors": [
|
||||||
|
{
|
||||||
|
"entity_id": entity,
|
||||||
|
"name": friendly_name_for_entity_id(entity, self.hass),
|
||||||
|
}
|
||||||
|
for (entity, state) in args["open_sensors"].items()
|
||||||
|
],
|
||||||
|
}
|
||||||
|
elif event == const.EVENT_COMMAND_NOT_ALLOWED:
|
||||||
|
payload = {
|
||||||
|
"event": event.upper(),
|
||||||
|
"state": args["state"],
|
||||||
|
"command": args["command"].upper(),
|
||||||
|
}
|
||||||
|
elif event in [
|
||||||
|
const.EVENT_INVALID_CODE_PROVIDED,
|
||||||
|
const.EVENT_NO_CODE_PROVIDED,
|
||||||
|
]:
|
||||||
|
payload = {"event": event.upper()}
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
payload = json.dumps(payload, cls=JSONEncoder)
|
||||||
|
hass.async_create_task(mqtt.async_publish(self.hass, topic, payload))
|
||||||
|
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(self.hass, "alarmo_event", async_handle_event)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Prepare for removal."""
|
||||||
|
while len(self._subscribed_topics):
|
||||||
|
self._subscribed_topics.pop()()
|
||||||
|
while len(self._subscriptions):
|
||||||
|
self._subscriptions.pop()()
|
||||||
|
|
||||||
|
async def _async_subscribe_topics(self):
|
||||||
|
"""Install a listener for the command topic."""
|
||||||
|
if len(self._subscribed_topics):
|
||||||
|
while len(self._subscribed_topics):
|
||||||
|
self._subscribed_topics.pop()()
|
||||||
|
_LOGGER.debug("Removed subscribed topics")
|
||||||
|
|
||||||
|
if not self._config[ATTR_MQTT][const.ATTR_ENABLED]:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._subscribed_topics.append(
|
||||||
|
await mqtt.async_subscribe(
|
||||||
|
self.hass,
|
||||||
|
self._config[ATTR_MQTT][CONF_COMMAND_TOPIC],
|
||||||
|
self.async_message_received,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Subscribed to topic %s",
|
||||||
|
self._config[ATTR_MQTT][CONF_COMMAND_TOPIC],
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
async def async_message_received(self, msg): # noqa: PLR0915, PLR0912
|
||||||
|
"""Handle new MQTT messages."""
|
||||||
|
command = None
|
||||||
|
code = None
|
||||||
|
area = None
|
||||||
|
bypass_open_sensors = False
|
||||||
|
skip_delay = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
payload = json.loads(msg.payload)
|
||||||
|
payload = {k.lower(): v for k, v in payload.items()}
|
||||||
|
|
||||||
|
if "command" in payload:
|
||||||
|
command = payload["command"]
|
||||||
|
elif "cmd" in payload:
|
||||||
|
command = payload["cmd"]
|
||||||
|
elif "action" in payload:
|
||||||
|
command = payload["action"]
|
||||||
|
elif "state" in payload:
|
||||||
|
command = payload["state"]
|
||||||
|
|
||||||
|
if "code" in payload:
|
||||||
|
code = payload["code"]
|
||||||
|
elif "pin" in payload:
|
||||||
|
code = payload["pin"]
|
||||||
|
elif "password" in payload:
|
||||||
|
code = payload["password"]
|
||||||
|
elif "pincode" in payload:
|
||||||
|
code = payload["pincode"]
|
||||||
|
|
||||||
|
if payload.get("area"):
|
||||||
|
area = payload["area"]
|
||||||
|
|
||||||
|
if (payload.get("bypass_open_sensors")) or (payload.get("force")):
|
||||||
|
bypass_open_sensors = payload["bypass_open_sensors"]
|
||||||
|
|
||||||
|
if payload.get(const.ATTR_SKIP_DELAY):
|
||||||
|
skip_delay = payload[const.ATTR_SKIP_DELAY]
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
# no JSON structure found
|
||||||
|
command = msg.payload
|
||||||
|
code = None
|
||||||
|
|
||||||
|
if type(command) is str:
|
||||||
|
command = command.lower()
|
||||||
|
else:
|
||||||
|
_LOGGER.warning("Received unexpected command")
|
||||||
|
return
|
||||||
|
|
||||||
|
payload_config = self._config[ATTR_MQTT][const.ATTR_COMMAND_PAYLOAD]
|
||||||
|
skip_code = not self._config[ATTR_MQTT][const.ATTR_REQUIRE_CODE]
|
||||||
|
|
||||||
|
command_payloads = {}
|
||||||
|
for item in const.COMMANDS:
|
||||||
|
if payload_config.get(item):
|
||||||
|
command_payloads[item] = payload_config[item].lower()
|
||||||
|
else:
|
||||||
|
command_payloads[item] = item.lower()
|
||||||
|
|
||||||
|
if command not in list(command_payloads.values()):
|
||||||
|
_LOGGER.warning("Received unexpected command: %s", command)
|
||||||
|
return
|
||||||
|
|
||||||
|
if area:
|
||||||
|
res = list(
|
||||||
|
filter(
|
||||||
|
lambda el: slugify(el.name) == area,
|
||||||
|
self.hass.data[const.DOMAIN]["areas"].values(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not res:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Area %s does not exist",
|
||||||
|
area,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
entity = res[0]
|
||||||
|
elif (
|
||||||
|
self._config[const.ATTR_MASTER][const.ATTR_ENABLED]
|
||||||
|
and len(self.hass.data[const.DOMAIN]["areas"]) > 1
|
||||||
|
):
|
||||||
|
entity = self.hass.data[const.DOMAIN]["master"]
|
||||||
|
elif len(self.hass.data[const.DOMAIN]["areas"]) == 1:
|
||||||
|
entity = next(iter(self.hass.data[const.DOMAIN]["areas"].values()))
|
||||||
|
else:
|
||||||
|
_LOGGER.warning("No area specified")
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Received command %s",
|
||||||
|
command,
|
||||||
|
)
|
||||||
|
|
||||||
|
if command == command_payloads[const.COMMAND_DISARM]:
|
||||||
|
entity.alarm_disarm(code, skip_code=skip_code)
|
||||||
|
elif command == command_payloads[const.COMMAND_ARM_AWAY]:
|
||||||
|
await entity.async_alarm_arm_away(
|
||||||
|
code, skip_code, bypass_open_sensors, skip_delay
|
||||||
|
)
|
||||||
|
elif command == command_payloads[const.COMMAND_ARM_NIGHT]:
|
||||||
|
await entity.async_alarm_arm_night(
|
||||||
|
code, skip_code, bypass_open_sensors, skip_delay
|
||||||
|
)
|
||||||
|
elif command == command_payloads[const.COMMAND_ARM_HOME]:
|
||||||
|
await entity.async_alarm_arm_home(
|
||||||
|
code, skip_code, bypass_open_sensors, skip_delay
|
||||||
|
)
|
||||||
|
elif command == command_payloads[const.COMMAND_ARM_CUSTOM_BYPASS]:
|
||||||
|
await entity.async_alarm_arm_custom_bypass(
|
||||||
|
code, skip_code, bypass_open_sensors, skip_delay
|
||||||
|
)
|
||||||
|
elif command == command_payloads[const.COMMAND_ARM_VACATION]:
|
||||||
|
await entity.async_alarm_arm_vacation(
|
||||||
|
code, skip_code, bypass_open_sensors, skip_delay
|
||||||
|
)
|
||||||
50
custom_components/alarmo/panel.py
Normal file
50
custom_components/alarmo/panel.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"""Panel registration for Alarmo integration."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.components import frontend, panel_custom
|
||||||
|
from homeassistant.components.http import StaticPathConfig
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
DOMAIN,
|
||||||
|
PANEL_URL,
|
||||||
|
PANEL_ICON,
|
||||||
|
PANEL_NAME,
|
||||||
|
PANEL_TITLE,
|
||||||
|
PANEL_FOLDER,
|
||||||
|
PANEL_FILENAME,
|
||||||
|
CUSTOM_COMPONENTS,
|
||||||
|
INTEGRATION_FOLDER,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_register_panel(hass):
|
||||||
|
"""Register the panel."""
|
||||||
|
root_dir = os.path.join(hass.config.path(CUSTOM_COMPONENTS), INTEGRATION_FOLDER)
|
||||||
|
panel_dir = os.path.join(root_dir, PANEL_FOLDER)
|
||||||
|
view_url = os.path.join(panel_dir, PANEL_FILENAME)
|
||||||
|
|
||||||
|
await hass.http.async_register_static_paths(
|
||||||
|
[StaticPathConfig(PANEL_URL, view_url, cache_headers=False)]
|
||||||
|
)
|
||||||
|
|
||||||
|
await panel_custom.async_register_panel(
|
||||||
|
hass,
|
||||||
|
webcomponent_name=PANEL_NAME,
|
||||||
|
frontend_url_path=DOMAIN,
|
||||||
|
module_url=PANEL_URL,
|
||||||
|
sidebar_title=PANEL_TITLE,
|
||||||
|
sidebar_icon=PANEL_ICON,
|
||||||
|
require_admin=True,
|
||||||
|
config={},
|
||||||
|
config_panel_domain=DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def async_unregister_panel(hass):
|
||||||
|
"""Unregister the panel."""
|
||||||
|
frontend.async_remove_panel(hass, DOMAIN)
|
||||||
|
_LOGGER.debug("Removing panel")
|
||||||
680
custom_components/alarmo/sensors.py
Normal file
680
custom_components/alarmo/sensors.py
Normal file
@@ -0,0 +1,680 @@
|
|||||||
|
"""Sensor handling for Alarmo integration."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
from homeassistant.core import (
|
||||||
|
CoreState,
|
||||||
|
HomeAssistant,
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
STATE_ON,
|
||||||
|
ATTR_NAME,
|
||||||
|
STATE_OFF,
|
||||||
|
ATTR_STATE,
|
||||||
|
STATE_OPEN,
|
||||||
|
STATE_CLOSED,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
ATTR_LAST_TRIP_TIME,
|
||||||
|
EVENT_HOMEASSISTANT_STARTED,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.event import (
|
||||||
|
async_track_point_in_time,
|
||||||
|
async_track_state_change_event,
|
||||||
|
)
|
||||||
|
from homeassistant.components.lock import LockState
|
||||||
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
async_dispatcher_connect,
|
||||||
|
)
|
||||||
|
from homeassistant.components.alarm_control_panel import AlarmControlPanelState
|
||||||
|
|
||||||
|
from . import const
|
||||||
|
|
||||||
|
ATTR_USE_EXIT_DELAY = "use_exit_delay"
|
||||||
|
ATTR_USE_ENTRY_DELAY = "use_entry_delay"
|
||||||
|
ATTR_ALWAYS_ON = "always_on"
|
||||||
|
ATTR_ARM_ON_CLOSE = "arm_on_close"
|
||||||
|
ATTR_ALLOW_OPEN = "allow_open"
|
||||||
|
ATTR_TRIGGER_UNAVAILABLE = "trigger_unavailable"
|
||||||
|
ATTR_AUTO_BYPASS = "auto_bypass"
|
||||||
|
ATTR_AUTO_BYPASS_MODES = "auto_bypass_modes"
|
||||||
|
ATTR_GROUP = "group"
|
||||||
|
ATTR_GROUP_ID = "group_id"
|
||||||
|
ATTR_TIMEOUT = "timeout"
|
||||||
|
ATTR_EVENT_COUNT = "event_count"
|
||||||
|
ATTR_ENTITIES = "entities"
|
||||||
|
ATTR_NEW_ENTITY_ID = "new_entity_id"
|
||||||
|
ATTR_ENTRY_DELAY = "entry_delay"
|
||||||
|
|
||||||
|
SENSOR_STATES_OPEN = [STATE_ON, STATE_OPEN, LockState.UNLOCKED]
|
||||||
|
SENSOR_STATES_CLOSED = [STATE_OFF, STATE_CLOSED, LockState.LOCKED]
|
||||||
|
|
||||||
|
|
||||||
|
SENSOR_TYPE_DOOR = "door"
|
||||||
|
SENSOR_TYPE_WINDOW = "window"
|
||||||
|
SENSOR_TYPE_MOTION = "motion"
|
||||||
|
SENSOR_TYPE_TAMPER = "tamper"
|
||||||
|
SENSOR_TYPE_ENVIRONMENTAL = "environmental"
|
||||||
|
SENSOR_TYPE_OTHER = "other"
|
||||||
|
SENSOR_TYPES = [
|
||||||
|
SENSOR_TYPE_DOOR,
|
||||||
|
SENSOR_TYPE_WINDOW,
|
||||||
|
SENSOR_TYPE_MOTION,
|
||||||
|
SENSOR_TYPE_TAMPER,
|
||||||
|
SENSOR_TYPE_ENVIRONMENTAL,
|
||||||
|
SENSOR_TYPE_OTHER,
|
||||||
|
]
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_sensor_state(state):
|
||||||
|
"""Parse the state of a sensor into open/closed/unavailable/unknown."""
|
||||||
|
if not state or not state.state:
|
||||||
|
return STATE_UNAVAILABLE
|
||||||
|
elif state.state == STATE_UNAVAILABLE:
|
||||||
|
return STATE_UNAVAILABLE
|
||||||
|
elif state.state in SENSOR_STATES_OPEN:
|
||||||
|
return STATE_OPEN
|
||||||
|
elif state.state in SENSOR_STATES_CLOSED:
|
||||||
|
return STATE_CLOSED
|
||||||
|
else:
|
||||||
|
return STATE_UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
def sensor_state_allowed(state, sensor_config, alarm_state): # noqa: PLR0911
|
||||||
|
"""Return whether the sensor state is permitted or a state change should occur."""
|
||||||
|
if state != STATE_OPEN and (
|
||||||
|
state != STATE_UNAVAILABLE or not sensor_config[ATTR_TRIGGER_UNAVAILABLE]
|
||||||
|
):
|
||||||
|
# sensor has the safe state
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif alarm_state == AlarmControlPanelState.TRIGGERED:
|
||||||
|
# alarm is already triggered
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif sensor_config[ATTR_ALWAYS_ON]:
|
||||||
|
# alarm should always be triggered by always-on sensor
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif (
|
||||||
|
alarm_state == AlarmControlPanelState.ARMING
|
||||||
|
and not sensor_config[ATTR_USE_EXIT_DELAY]
|
||||||
|
):
|
||||||
|
# arming should be aborted if sensor without exit delay is active
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif alarm_state in const.ARM_MODES:
|
||||||
|
# normal triggering case
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif alarm_state == AlarmControlPanelState.PENDING:
|
||||||
|
# Allow both immediate and delayed sensors
|
||||||
|
# during pending for timer shortening/immediate trigger
|
||||||
|
# This enables per-sensor entry delay logic
|
||||||
|
# to process subsequent triggers during countdown
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class SensorHandler:
|
||||||
|
"""Class to handle sensors for Alarmo."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant):
|
||||||
|
"""Initialize the sensor handler."""
|
||||||
|
self._config = None
|
||||||
|
self.hass = hass
|
||||||
|
self._state_listener = None
|
||||||
|
self._subscriptions = []
|
||||||
|
self._arm_timers = {}
|
||||||
|
self._groups = {}
|
||||||
|
self._group_events = {}
|
||||||
|
self._startup_complete = False
|
||||||
|
self._unavailable_state_mem = {}
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_sensor_config():
|
||||||
|
"""Sensor config updated, reload the configuration."""
|
||||||
|
self._config = self.hass.data[const.DOMAIN][
|
||||||
|
"coordinator"
|
||||||
|
].store.async_get_sensors()
|
||||||
|
self._groups = self.hass.data[const.DOMAIN][
|
||||||
|
"coordinator"
|
||||||
|
].store.async_get_sensor_groups()
|
||||||
|
self._group_events = {}
|
||||||
|
self.async_watch_sensor_states()
|
||||||
|
|
||||||
|
# Store the callback for later registration
|
||||||
|
self._async_update_sensor_config = async_update_sensor_config
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _setup_sensor_listeners():
|
||||||
|
"""Register sensor listeners and perform initial setup."""
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
hass, "alarmo_state_updated", self.async_watch_sensor_states
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._subscriptions.append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
hass, "alarmo_sensors_updated", self._async_update_sensor_config
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Do the initial sensor setup now that HA is running
|
||||||
|
self._async_update_sensor_config()
|
||||||
|
|
||||||
|
# Evaluate initial sensor states for all areas on startup
|
||||||
|
for area_id in self.hass.data[const.DOMAIN]["areas"].keys():
|
||||||
|
self.update_ready_to_arm_status(area_id)
|
||||||
|
# If area is armed, validate sensors and trigger if needed
|
||||||
|
# Schedule this to run in the event loop since it may call async methods
|
||||||
|
hass.async_create_task(
|
||||||
|
self._async_evaluate_armed_state_on_startup(area_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_startup(_event):
|
||||||
|
self._startup_complete = True
|
||||||
|
# Schedule the setup to run in the event loop (from thread pool executor)
|
||||||
|
hass.loop.call_soon_threadsafe(_setup_sensor_listeners)
|
||||||
|
|
||||||
|
if hass.state == CoreState.running:
|
||||||
|
self._startup_complete = True
|
||||||
|
# Schedule in event loop since we're in __init__ (sync context)
|
||||||
|
hass.loop.call_soon_threadsafe(_setup_sensor_listeners)
|
||||||
|
else:
|
||||||
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, handle_startup)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""Prepare for removal."""
|
||||||
|
if self._state_listener:
|
||||||
|
self._state_listener()
|
||||||
|
self._state_listener = None
|
||||||
|
while len(self._subscriptions):
|
||||||
|
self._subscriptions.pop()()
|
||||||
|
|
||||||
|
def async_watch_sensor_states(
|
||||||
|
self,
|
||||||
|
area_id: str | None = None,
|
||||||
|
old_state: str | None = None,
|
||||||
|
state: str | None = None,
|
||||||
|
):
|
||||||
|
"""Watch sensors based on the state of the alarm entities."""
|
||||||
|
sensors_list = []
|
||||||
|
for area in self.hass.data[const.DOMAIN]["areas"].keys():
|
||||||
|
sensors_list.extend(self.active_sensors_for_alarm_state(area))
|
||||||
|
|
||||||
|
if self._state_listener:
|
||||||
|
self._state_listener()
|
||||||
|
|
||||||
|
if sensors_list:
|
||||||
|
self._state_listener = async_track_state_change_event(
|
||||||
|
self.hass, sensors_list, self.async_sensor_state_changed
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._state_listener = None
|
||||||
|
|
||||||
|
# clear previous sensor group events that are not active for current alarm state
|
||||||
|
for group_id in self._group_events.keys():
|
||||||
|
self._group_events[group_id] = dict(
|
||||||
|
filter(
|
||||||
|
lambda el: el[0] in sensors_list,
|
||||||
|
self._group_events[group_id].items(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# handle initial sensor states
|
||||||
|
if area_id and old_state is None:
|
||||||
|
sensors_list = self.active_sensors_for_alarm_state(area_id)
|
||||||
|
for entity in sensors_list:
|
||||||
|
state = self.hass.states.get(entity)
|
||||||
|
sensor_state = parse_sensor_state(state)
|
||||||
|
if state and state.state and sensor_state != STATE_UNKNOWN:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Initial state for %s is %s",
|
||||||
|
entity,
|
||||||
|
parse_sensor_state(state),
|
||||||
|
)
|
||||||
|
|
||||||
|
if area_id:
|
||||||
|
self.update_ready_to_arm_status(area_id)
|
||||||
|
|
||||||
|
def active_sensors_for_alarm_state(self, area_id: str, to_state: str | None = None):
|
||||||
|
"""Compose a list of sensors that are active for the state."""
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
|
||||||
|
if to_state:
|
||||||
|
state = to_state
|
||||||
|
else:
|
||||||
|
state = (
|
||||||
|
alarm_entity.arm_mode if alarm_entity.arm_mode else alarm_entity.state
|
||||||
|
)
|
||||||
|
|
||||||
|
entities = []
|
||||||
|
for entity, config in self._config.items():
|
||||||
|
if config["area"] != area_id or not config["enabled"]:
|
||||||
|
continue
|
||||||
|
elif (
|
||||||
|
alarm_entity.bypassed_sensors
|
||||||
|
and entity in alarm_entity.bypassed_sensors
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
elif state in config[const.ATTR_MODES] or config[ATTR_ALWAYS_ON]:
|
||||||
|
entities.append(entity)
|
||||||
|
elif not to_state and config["type"] != SENSOR_TYPE_MOTION:
|
||||||
|
# always watch all sensors other than motion sensors,
|
||||||
|
# to indicate readiness for arming
|
||||||
|
entities.append(entity)
|
||||||
|
|
||||||
|
return entities
|
||||||
|
|
||||||
|
def validate_arming_event(
|
||||||
|
self, area_id: str, target_state: str | None = None, **kwargs
|
||||||
|
):
|
||||||
|
"""Check whether all sensors have the correct state prior to arming."""
|
||||||
|
use_delay = kwargs.get("use_delay", False)
|
||||||
|
bypass_open_sensors = kwargs.get("bypass_open_sensors", False)
|
||||||
|
|
||||||
|
sensors_list = self.active_sensors_for_alarm_state(area_id, target_state)
|
||||||
|
open_sensors = {}
|
||||||
|
bypassed_sensors = []
|
||||||
|
|
||||||
|
alarm_state = target_state
|
||||||
|
if use_delay and alarm_state in const.ARM_MODES:
|
||||||
|
alarm_state = AlarmControlPanelState.ARMING
|
||||||
|
elif use_delay and alarm_state == AlarmControlPanelState.TRIGGERED:
|
||||||
|
alarm_state = AlarmControlPanelState.PENDING
|
||||||
|
|
||||||
|
for entity in sensors_list:
|
||||||
|
sensor_config = self._config[entity]
|
||||||
|
state = self.hass.states.get(entity)
|
||||||
|
sensor_state = parse_sensor_state(state)
|
||||||
|
if not state or not state.state:
|
||||||
|
# entity does not exist in HA
|
||||||
|
res = False
|
||||||
|
else:
|
||||||
|
res = sensor_state_allowed(sensor_state, sensor_config, alarm_state)
|
||||||
|
|
||||||
|
if not res and target_state in const.ARM_MODES:
|
||||||
|
# sensor is active while arming
|
||||||
|
if bypass_open_sensors or (
|
||||||
|
sensor_config[ATTR_AUTO_BYPASS]
|
||||||
|
and target_state in sensor_config[ATTR_AUTO_BYPASS_MODES]
|
||||||
|
):
|
||||||
|
# sensor may be bypassed
|
||||||
|
bypassed_sensors.append(entity)
|
||||||
|
elif sensor_config[ATTR_ALLOW_OPEN] and sensor_state == STATE_OPEN:
|
||||||
|
# sensor is permitted to be open during/after arming
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
open_sensors[entity] = sensor_state
|
||||||
|
|
||||||
|
return (open_sensors, bypassed_sensors)
|
||||||
|
|
||||||
|
def get_entry_delay_for_trigger(
|
||||||
|
self, open_sensors: dict[str, str], area_id: str, arm_mode: str
|
||||||
|
) -> int | None:
|
||||||
|
"""Calculate entry delay based on type of sensor trigger."""
|
||||||
|
# Check if this is a group trigger
|
||||||
|
if ATTR_GROUP_ID in open_sensors:
|
||||||
|
# For groups: only check for immediate triggers, otherwise use area default
|
||||||
|
for entity_id in open_sensors:
|
||||||
|
if entity_id != ATTR_GROUP_ID and entity_id in self._config:
|
||||||
|
sensor_config = self._config[entity_id]
|
||||||
|
if not sensor_config[ATTR_USE_ENTRY_DELAY]:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Groups always use area default (maintainer's preference)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
# Individual sensor trigger
|
||||||
|
entity_id = next(iter(open_sensors.keys()))
|
||||||
|
sensor_config = self._config[entity_id]
|
||||||
|
|
||||||
|
if not sensor_config[ATTR_USE_ENTRY_DELAY]:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Use sensor's entry delay if set
|
||||||
|
if (
|
||||||
|
ATTR_ENTRY_DELAY in sensor_config
|
||||||
|
and sensor_config[ATTR_ENTRY_DELAY] is not None
|
||||||
|
):
|
||||||
|
return sensor_config[ATTR_ENTRY_DELAY]
|
||||||
|
|
||||||
|
# Fall back to area default (None means use area default)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_sensor_state_changed(self, event): # noqa: PLR0915, PLR0912
|
||||||
|
"""Callback fired when a sensor state has changed."""
|
||||||
|
entity = event.data["entity_id"]
|
||||||
|
old_state = parse_sensor_state(event.data["old_state"])
|
||||||
|
new_state = parse_sensor_state(event.data["new_state"])
|
||||||
|
sensor_config = self._config[entity]
|
||||||
|
if old_state == STATE_UNKNOWN:
|
||||||
|
# sensor is unknown at startup,
|
||||||
|
# state which comes after is considered as initial state
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Initial state for %s is %s",
|
||||||
|
entity,
|
||||||
|
new_state,
|
||||||
|
)
|
||||||
|
self.update_ready_to_arm_status(sensor_config["area"])
|
||||||
|
return
|
||||||
|
if old_state == new_state:
|
||||||
|
# not a state change - ignore
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"entity %s changed: old_state=%s, new_state=%s",
|
||||||
|
entity,
|
||||||
|
old_state,
|
||||||
|
new_state,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
new_state == STATE_UNAVAILABLE
|
||||||
|
and not sensor_config[ATTR_TRIGGER_UNAVAILABLE]
|
||||||
|
):
|
||||||
|
# temporarily store the prior state until the sensor becomes available again
|
||||||
|
self._unavailable_state_mem[entity] = old_state
|
||||||
|
elif entity in self._unavailable_state_mem:
|
||||||
|
# if sensor was unavailable, check the state before that,
|
||||||
|
# do not act if the sensor reverted to its prior state.
|
||||||
|
prior_state = self._unavailable_state_mem.pop(entity)
|
||||||
|
if old_state == STATE_UNAVAILABLE and prior_state == new_state:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"state transition from %s to %s to %s detected, ignoring.",
|
||||||
|
prior_state,
|
||||||
|
old_state,
|
||||||
|
new_state,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][sensor_config["area"]]
|
||||||
|
alarm_state = alarm_entity.state
|
||||||
|
|
||||||
|
if (
|
||||||
|
alarm_entity.arm_mode
|
||||||
|
and alarm_entity.arm_mode not in sensor_config[const.ATTR_MODES]
|
||||||
|
and not sensor_config[ATTR_ALWAYS_ON]
|
||||||
|
):
|
||||||
|
# sensor is not active in this arm mode, ignore
|
||||||
|
self.update_ready_to_arm_status(sensor_config["area"])
|
||||||
|
return
|
||||||
|
|
||||||
|
res = sensor_state_allowed(new_state, sensor_config, alarm_state)
|
||||||
|
|
||||||
|
if (
|
||||||
|
sensor_config[ATTR_ARM_ON_CLOSE]
|
||||||
|
and alarm_state == AlarmControlPanelState.ARMING
|
||||||
|
):
|
||||||
|
# we are arming and sensor is configured to arm on closing
|
||||||
|
if new_state == STATE_CLOSED:
|
||||||
|
self.start_arm_timer(entity)
|
||||||
|
else:
|
||||||
|
self.stop_arm_timer(entity)
|
||||||
|
|
||||||
|
if res:
|
||||||
|
# sensor state is OK,
|
||||||
|
# but we still need to clean up group events for closed sensors
|
||||||
|
# A sensor that has closed should not contribute to future group triggers
|
||||||
|
# until it opens again
|
||||||
|
# Clear closed sensors from group events to
|
||||||
|
# prevent stale events from triggering groups later
|
||||||
|
if new_state == STATE_CLOSED:
|
||||||
|
for group_id in list(self._group_events.keys()):
|
||||||
|
if entity in self._group_events[group_id]:
|
||||||
|
del self._group_events[group_id][entity]
|
||||||
|
# Clean up empty group entries
|
||||||
|
if not self._group_events[group_id]:
|
||||||
|
del self._group_events[group_id]
|
||||||
|
self.update_ready_to_arm_status(sensor_config["area"])
|
||||||
|
return
|
||||||
|
|
||||||
|
open_sensors = self.process_group_event(entity, new_state)
|
||||||
|
if not open_sensors:
|
||||||
|
# triggered sensor is part of a group and should be ignored
|
||||||
|
self.update_ready_to_arm_status(sensor_config["area"])
|
||||||
|
return
|
||||||
|
|
||||||
|
if sensor_config[ATTR_ALWAYS_ON]:
|
||||||
|
# immediate trigger due to always on sensor
|
||||||
|
_LOGGER.info(
|
||||||
|
"Alarm is triggered due to an always-on sensor: %s",
|
||||||
|
entity,
|
||||||
|
)
|
||||||
|
alarm_entity.async_trigger(entry_delay=0, open_sensors=open_sensors)
|
||||||
|
|
||||||
|
elif alarm_state == AlarmControlPanelState.ARMING:
|
||||||
|
# sensor triggered while arming, abort arming
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Arming was aborted due to a sensor being active: %s",
|
||||||
|
entity,
|
||||||
|
)
|
||||||
|
alarm_entity.async_arm_failure(open_sensors)
|
||||||
|
|
||||||
|
elif alarm_state in const.ARM_MODES:
|
||||||
|
# standard alarm trigger - calculate entry delay override
|
||||||
|
_LOGGER.info(
|
||||||
|
"Alarm is triggered due to sensor: %s",
|
||||||
|
entity,
|
||||||
|
)
|
||||||
|
entry_delay = self.get_entry_delay_for_trigger(
|
||||||
|
open_sensors, sensor_config["area"], alarm_entity.arm_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
if entry_delay == 0:
|
||||||
|
# immediate trigger (no entry delay)
|
||||||
|
alarm_entity.async_trigger(entry_delay=0, open_sensors=open_sensors)
|
||||||
|
else:
|
||||||
|
# use calculated delay (could be None for area default)
|
||||||
|
alarm_entity.async_trigger(
|
||||||
|
entry_delay=entry_delay, open_sensors=open_sensors
|
||||||
|
)
|
||||||
|
|
||||||
|
elif alarm_state == AlarmControlPanelState.PENDING:
|
||||||
|
# trigger while in pending state
|
||||||
|
# calculate entry delay for possible timer shortening
|
||||||
|
_LOGGER.info(
|
||||||
|
"Alarm is triggered due to sensor: %s",
|
||||||
|
entity,
|
||||||
|
)
|
||||||
|
entry_delay = self.get_entry_delay_for_trigger(
|
||||||
|
open_sensors, sensor_config["area"], alarm_entity.arm_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
if entry_delay == 0:
|
||||||
|
# immediate trigger
|
||||||
|
alarm_entity.async_trigger(entry_delay=0, open_sensors=open_sensors)
|
||||||
|
else:
|
||||||
|
# use calculated delay for possible timer shortening
|
||||||
|
alarm_entity.async_trigger(
|
||||||
|
entry_delay=entry_delay, open_sensors=open_sensors
|
||||||
|
)
|
||||||
|
|
||||||
|
self.update_ready_to_arm_status(sensor_config["area"])
|
||||||
|
|
||||||
|
def start_arm_timer(self, entity):
|
||||||
|
"""Start timer for automatical arming."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def timer_finished(now):
|
||||||
|
_LOGGER.debug("timer finished")
|
||||||
|
sensor_config = self._config[entity]
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][sensor_config["area"]]
|
||||||
|
if alarm_entity.state == AlarmControlPanelState.ARMING:
|
||||||
|
alarm_entity.async_arm(alarm_entity.arm_mode, skip_delay=True)
|
||||||
|
|
||||||
|
now = dt_util.utcnow()
|
||||||
|
|
||||||
|
if entity in self._arm_timers:
|
||||||
|
self.stop_arm_timer(entity)
|
||||||
|
|
||||||
|
self._arm_timers[entity] = async_track_point_in_time(
|
||||||
|
self.hass, timer_finished, now + const.SENSOR_ARM_TIME
|
||||||
|
)
|
||||||
|
|
||||||
|
def stop_arm_timer(self, entity=None):
|
||||||
|
"""Cancel timer(s) for automatical arming."""
|
||||||
|
if entity and entity in self._arm_timers:
|
||||||
|
self._arm_timers[entity]()
|
||||||
|
elif not entity:
|
||||||
|
for key in self._arm_timers.keys():
|
||||||
|
self._arm_timers[key]()
|
||||||
|
|
||||||
|
def process_group_event(self, entity: str, state: str) -> dict:
|
||||||
|
"""Check if sensor entity is member of a group to evaluate trigger."""
|
||||||
|
group_id = None
|
||||||
|
for group in self._groups.values():
|
||||||
|
if entity in group[ATTR_ENTITIES]:
|
||||||
|
group_id = group[ATTR_GROUP_ID]
|
||||||
|
break
|
||||||
|
|
||||||
|
open_sensors = {entity: state}
|
||||||
|
if group_id is None:
|
||||||
|
return open_sensors
|
||||||
|
|
||||||
|
group = self._groups[group_id]
|
||||||
|
group_events = (
|
||||||
|
self._group_events[group_id]
|
||||||
|
if group_id in self._group_events.keys()
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
now = dt_util.now()
|
||||||
|
group_events[entity] = {ATTR_STATE: state, ATTR_LAST_TRIP_TIME: now}
|
||||||
|
self._group_events[group_id] = group_events
|
||||||
|
recent_events = {
|
||||||
|
entity: (now - event[ATTR_LAST_TRIP_TIME]).total_seconds()
|
||||||
|
for (entity, event) in group_events.items()
|
||||||
|
}
|
||||||
|
recent_events = dict(
|
||||||
|
filter(lambda el: el[1] <= group[ATTR_TIMEOUT], recent_events.items())
|
||||||
|
)
|
||||||
|
if len(recent_events.keys()) < group[ATTR_EVENT_COUNT]:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"tripped sensor %s was ignored since it belongs to group %s",
|
||||||
|
entity,
|
||||||
|
group[ATTR_NAME],
|
||||||
|
)
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
for key in recent_events.keys():
|
||||||
|
open_sensors[key] = group_events[key][ATTR_STATE]
|
||||||
|
|
||||||
|
# Add group info for override delay calculation
|
||||||
|
open_sensors[ATTR_GROUP_ID] = group_id
|
||||||
|
_LOGGER.debug(
|
||||||
|
"tripped sensor %s caused the triggering of group %s",
|
||||||
|
entity,
|
||||||
|
group[ATTR_NAME],
|
||||||
|
)
|
||||||
|
return open_sensors
|
||||||
|
|
||||||
|
def update_ready_to_arm_status(self, area_id):
|
||||||
|
"""Calculate whether the system is ready for arming."""
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
|
||||||
|
arm_modes = [
|
||||||
|
mode
|
||||||
|
for (mode, config) in alarm_entity._config[const.ATTR_MODES].items()
|
||||||
|
if config[const.ATTR_ENABLED]
|
||||||
|
]
|
||||||
|
|
||||||
|
if alarm_entity.state in const.ARM_MODES or (
|
||||||
|
alarm_entity.state == AlarmControlPanelState.ARMING
|
||||||
|
and alarm_entity.arm_mode
|
||||||
|
):
|
||||||
|
arm_modes.remove(alarm_entity.arm_mode)
|
||||||
|
|
||||||
|
def arm_mode_is_ready(mode):
|
||||||
|
(blocking_sensors, _bypassed_sensors) = self.validate_arming_event(
|
||||||
|
area_id, mode
|
||||||
|
)
|
||||||
|
if alarm_entity.state == AlarmControlPanelState.DISARMED:
|
||||||
|
# exclude motion sensors when determining readiness
|
||||||
|
blocking_sensors = dict(
|
||||||
|
filter(
|
||||||
|
lambda el: self._config[el[0]]["type"] != SENSOR_TYPE_MOTION,
|
||||||
|
blocking_sensors.items(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result = not (blocking_sensors)
|
||||||
|
return result
|
||||||
|
|
||||||
|
arm_modes = list(filter(arm_mode_is_ready, arm_modes))
|
||||||
|
prev_arm_modes = alarm_entity._ready_to_arm_modes
|
||||||
|
|
||||||
|
if arm_modes != prev_arm_modes:
|
||||||
|
alarm_entity.update_ready_to_arm_modes(arm_modes)
|
||||||
|
|
||||||
|
async def _async_evaluate_armed_state_on_startup(self, area_id):
|
||||||
|
"""Evaluate sensors when alarm is armed on startup and trigger if necessary.
|
||||||
|
|
||||||
|
On startup, we don't know the actual previous state of sensors
|
||||||
|
(they might have changed while HA was down).
|
||||||
|
This method simulates state changes for all sensors currently in violation,
|
||||||
|
allowing the standard async_sensor_state_changed logic to re-evaluate them
|
||||||
|
with full group logic, entry delays, etc.
|
||||||
|
"""
|
||||||
|
alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id]
|
||||||
|
|
||||||
|
# Only evaluate if the alarm is in an armed state
|
||||||
|
if alarm_entity.state not in const.ARM_MODES:
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Evaluating sensors on startup for area %s (state: %s)",
|
||||||
|
area_id,
|
||||||
|
alarm_entity.state,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get all active sensors for the current armed mode
|
||||||
|
sensors_list = self.active_sensors_for_alarm_state(area_id)
|
||||||
|
|
||||||
|
for entity_id in sensors_list:
|
||||||
|
sensor_config = self._config[entity_id]
|
||||||
|
state = self.hass.states.get(entity_id)
|
||||||
|
sensor_state = parse_sensor_state(state)
|
||||||
|
|
||||||
|
if sensor_state == STATE_UNKNOWN:
|
||||||
|
# Skip unknown sensors - they'll be handled when they become known
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if sensor state is allowed in current alarm state
|
||||||
|
res = sensor_state_allowed(sensor_state, sensor_config, alarm_entity.state)
|
||||||
|
|
||||||
|
if not res:
|
||||||
|
# Sensor is in a violation state
|
||||||
|
# (open or unavailable when it shouldn't be)
|
||||||
|
# Simulate a state change to trigger standard processing
|
||||||
|
_LOGGER.info(
|
||||||
|
"Sensor %s is %s on startup while alarm is %s - simulating state change for evaluation", # noqa: E501
|
||||||
|
entity_id,
|
||||||
|
sensor_state,
|
||||||
|
alarm_entity.state,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a synthetic event that mimics
|
||||||
|
# a state change from closed to current state
|
||||||
|
# We use STATE_CLOSED as old state
|
||||||
|
# (not STATE_UNKNOWN which would trigger early return)
|
||||||
|
old_state = SimpleNamespace(state=STATE_CLOSED)
|
||||||
|
|
||||||
|
# Create event with the structure expected by async_sensor_state_changed
|
||||||
|
event = SimpleNamespace(
|
||||||
|
data={
|
||||||
|
"entity_id": entity_id,
|
||||||
|
"old_state": old_state,
|
||||||
|
"new_state": state,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process through the standard sensor state change handler
|
||||||
|
# This will handle groups, entry delays, always-on sensors, etc.
|
||||||
|
self.async_sensor_state_changed(event)
|
||||||
77
custom_components/alarmo/services.yaml
Normal file
77
custom_components/alarmo/services.yaml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
arm:
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
example: "alarm_control_panel.alarm"
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
integration: alarmo
|
||||||
|
domain: alarm_control_panel
|
||||||
|
code:
|
||||||
|
example: "1234"
|
||||||
|
required: false
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
mode:
|
||||||
|
example: "away"
|
||||||
|
required: false
|
||||||
|
default: away
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
translation_key: "arm_mode"
|
||||||
|
options:
|
||||||
|
- away
|
||||||
|
- night
|
||||||
|
- home
|
||||||
|
- vacation
|
||||||
|
- custom
|
||||||
|
skip_delay:
|
||||||
|
example: false
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
force:
|
||||||
|
example: false
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
disarm:
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
example: "alarm_control_panel.alarm"
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
integration: alarmo
|
||||||
|
domain: alarm_control_panel
|
||||||
|
code:
|
||||||
|
example: "1234"
|
||||||
|
required: false
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
skip_delay:
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
example: "alarm_control_panel.alarm"
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
integration: alarmo
|
||||||
|
domain: alarm_control_panel
|
||||||
|
enable_user:
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
example: "Frank"
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
disable_user:
|
||||||
|
fields:
|
||||||
|
name:
|
||||||
|
example: "Frank"
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
721
custom_components/alarmo/store.py
Normal file
721
custom_components/alarmo/store.py
Normal file
@@ -0,0 +1,721 @@
|
|||||||
|
"""Storage handler for Alarmo integration."""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
from typing import cast
|
||||||
|
from collections import OrderedDict
|
||||||
|
from collections.abc import MutableMapping
|
||||||
|
|
||||||
|
import attr
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.storage import Store
|
||||||
|
from homeassistant.components.alarm_control_panel import CodeFormat
|
||||||
|
|
||||||
|
from . import const
|
||||||
|
from .helpers import omit
|
||||||
|
from .sensors import (
|
||||||
|
SENSOR_TYPE_OTHER,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DATA_REGISTRY = f"{const.DOMAIN}_storage"
|
||||||
|
STORAGE_KEY = f"{const.DOMAIN}.storage"
|
||||||
|
STORAGE_VERSION_MAJOR = 6
|
||||||
|
STORAGE_VERSION_MINOR = 3
|
||||||
|
SAVE_DELAY = 10
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class ModeEntry:
|
||||||
|
"""Mode storage Entry."""
|
||||||
|
|
||||||
|
enabled = attr.ib(type=bool, default=False)
|
||||||
|
exit_time = attr.ib(type=int, default=None)
|
||||||
|
entry_time = attr.ib(type=int, default=None)
|
||||||
|
trigger_time = attr.ib(type=int, default=None)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class MqttConfig:
|
||||||
|
"""MQTT storage Entry."""
|
||||||
|
|
||||||
|
enabled = attr.ib(type=bool, default=False)
|
||||||
|
state_topic = attr.ib(type=str, default="alarmo/state")
|
||||||
|
state_payload = attr.ib(type=dict, default={})
|
||||||
|
command_topic = attr.ib(type=str, default="alarmo/command")
|
||||||
|
command_payload = attr.ib(type=dict, default={})
|
||||||
|
require_code = attr.ib(type=bool, default=True)
|
||||||
|
event_topic = attr.ib(type=str, default="alarmo/event")
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class MasterConfig:
|
||||||
|
"""Master storage Entry."""
|
||||||
|
|
||||||
|
enabled = attr.ib(type=bool, default=True)
|
||||||
|
name = attr.ib(type=str, default="master")
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class AreaEntry:
|
||||||
|
"""Area storage Entry."""
|
||||||
|
|
||||||
|
area_id = attr.ib(type=str, default=None)
|
||||||
|
name = attr.ib(type=str, default=None)
|
||||||
|
modes = attr.ib(
|
||||||
|
type=[str, ModeEntry],
|
||||||
|
default={
|
||||||
|
const.CONF_ALARM_ARMED_AWAY: ModeEntry(),
|
||||||
|
const.CONF_ALARM_ARMED_HOME: ModeEntry(),
|
||||||
|
const.CONF_ALARM_ARMED_NIGHT: ModeEntry(),
|
||||||
|
const.CONF_ALARM_ARMED_CUSTOM_BYPASS: ModeEntry(),
|
||||||
|
const.CONF_ALARM_ARMED_VACATION: ModeEntry(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class Config:
|
||||||
|
"""(General) Config storage Entry."""
|
||||||
|
|
||||||
|
code_arm_required = attr.ib(type=bool, default=False)
|
||||||
|
code_mode_change_required = attr.ib(type=bool, default=False)
|
||||||
|
code_disarm_required = attr.ib(type=bool, default=False)
|
||||||
|
code_format = attr.ib(type=str, default=CodeFormat.NUMBER)
|
||||||
|
disarm_after_trigger = attr.ib(type=bool, default=False)
|
||||||
|
ignore_blocking_sensors_after_trigger = attr.ib(type=bool, default=False)
|
||||||
|
master = attr.ib(type=MasterConfig, default=MasterConfig())
|
||||||
|
mqtt = attr.ib(type=MqttConfig, default=MqttConfig())
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class SensorEntry:
|
||||||
|
"""Sensor storage Entry."""
|
||||||
|
|
||||||
|
entity_id = attr.ib(type=str, default=None)
|
||||||
|
type = attr.ib(type=str, default=SENSOR_TYPE_OTHER)
|
||||||
|
modes = attr.ib(type=list, default=[])
|
||||||
|
use_exit_delay = attr.ib(type=bool, default=True)
|
||||||
|
use_entry_delay = attr.ib(type=bool, default=True)
|
||||||
|
always_on = attr.ib(type=bool, default=False)
|
||||||
|
arm_on_close = attr.ib(type=bool, default=False)
|
||||||
|
allow_open = attr.ib(type=bool, default=False)
|
||||||
|
trigger_unavailable = attr.ib(type=bool, default=False)
|
||||||
|
auto_bypass = attr.ib(type=bool, default=False)
|
||||||
|
auto_bypass_modes = attr.ib(type=list, default=[])
|
||||||
|
area = attr.ib(type=str, default=None)
|
||||||
|
enabled = attr.ib(type=bool, default=True)
|
||||||
|
entry_delay = attr.ib(type=int, default=None)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class UserEntry:
|
||||||
|
"""User storage Entry."""
|
||||||
|
|
||||||
|
user_id = attr.ib(type=str, default=None)
|
||||||
|
name = attr.ib(type=str, default="")
|
||||||
|
enabled = attr.ib(type=bool, default=True)
|
||||||
|
code = attr.ib(type=str, default="")
|
||||||
|
can_arm = attr.ib(type=bool, default=False)
|
||||||
|
can_disarm = attr.ib(type=bool, default=False)
|
||||||
|
is_override_code = attr.ib(type=bool, default=False)
|
||||||
|
code_format = attr.ib(type=str, default="")
|
||||||
|
code_length = attr.ib(type=int, default=0)
|
||||||
|
area_limit = attr.ib(type=list, default=[])
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class AlarmoTriggerEntry:
|
||||||
|
"""Trigger storage Entry."""
|
||||||
|
|
||||||
|
event = attr.ib(type=str, default="")
|
||||||
|
area = attr.ib(type=str, default=None)
|
||||||
|
modes = attr.ib(type=list, default=[])
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class EntityTriggerEntry:
|
||||||
|
"""Trigger storage Entry."""
|
||||||
|
|
||||||
|
entity_id = attr.ib(type=str, default=None)
|
||||||
|
state = attr.ib(type=str, default=None)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class ActionEntry:
|
||||||
|
"""Action storage Entry."""
|
||||||
|
|
||||||
|
service = attr.ib(type=str, default="")
|
||||||
|
entity_id = attr.ib(type=str, default=None)
|
||||||
|
data = attr.ib(type=dict, default={})
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class AutomationEntry:
|
||||||
|
"""Automation storage Entry."""
|
||||||
|
|
||||||
|
automation_id = attr.ib(type=str, default=None)
|
||||||
|
type = attr.ib(type=str, default=None)
|
||||||
|
name = attr.ib(type=str, default="")
|
||||||
|
triggers = attr.ib(type=[AlarmoTriggerEntry], default=[])
|
||||||
|
actions = attr.ib(type=[ActionEntry], default=[])
|
||||||
|
enabled = attr.ib(type=bool, default=True)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(slots=True, frozen=True)
|
||||||
|
class SensorGroupEntry:
|
||||||
|
"""Sensor group storage Entry."""
|
||||||
|
|
||||||
|
group_id = attr.ib(type=str, default=None)
|
||||||
|
name = attr.ib(type=str, default="")
|
||||||
|
entities = attr.ib(type=list, default=[])
|
||||||
|
timeout = attr.ib(type=int, default=0)
|
||||||
|
event_count = attr.ib(type=int, default=2)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_automation_entry(data: dict):
|
||||||
|
"""Parse automation entry from dict to proper types."""
|
||||||
|
|
||||||
|
def create_trigger_entity(config: dict):
|
||||||
|
if "event" in config:
|
||||||
|
return AlarmoTriggerEntry(**config)
|
||||||
|
else:
|
||||||
|
return EntityTriggerEntry(**config)
|
||||||
|
|
||||||
|
output = {}
|
||||||
|
if "triggers" in data:
|
||||||
|
output["triggers"] = list(map(create_trigger_entity, data["triggers"]))
|
||||||
|
if "actions" in data:
|
||||||
|
output["actions"] = list(map(lambda el: ActionEntry(**el), data["actions"]))
|
||||||
|
if "automation_id" in data:
|
||||||
|
output["automation_id"] = data["automation_id"]
|
||||||
|
if "name" in data:
|
||||||
|
output["name"] = data["name"]
|
||||||
|
if "type" in data:
|
||||||
|
output["type"] = data["type"]
|
||||||
|
if "enabled" in data:
|
||||||
|
output["enabled"] = data["enabled"]
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class MigratableStore(Store):
|
||||||
|
"""Storage class that can migrate data between versions."""
|
||||||
|
|
||||||
|
async def _async_migrate_func(
|
||||||
|
self, old_major_version: int, old_minor_version: int, data: dict
|
||||||
|
):
|
||||||
|
def migrate_automation(data):
|
||||||
|
if old_major_version <= 2:
|
||||||
|
data["triggers"] = [
|
||||||
|
{
|
||||||
|
"event": el["state"] if "state" in el else el["event"],
|
||||||
|
"area": el.get("area"),
|
||||||
|
"modes": data["modes"],
|
||||||
|
}
|
||||||
|
for el in data["triggers"]
|
||||||
|
]
|
||||||
|
|
||||||
|
data["type"] = (
|
||||||
|
"notification" if data.get("is_notification") else "action"
|
||||||
|
)
|
||||||
|
|
||||||
|
if old_major_version <= 5:
|
||||||
|
data["actions"] = [
|
||||||
|
{
|
||||||
|
"service": el.get("service"),
|
||||||
|
"entity_id": el.get("entity_id"),
|
||||||
|
"data": el.get("service_data"),
|
||||||
|
}
|
||||||
|
for el in data["actions"]
|
||||||
|
]
|
||||||
|
|
||||||
|
return attr.asdict(AutomationEntry(**parse_automation_entry(data)))
|
||||||
|
|
||||||
|
if old_major_version == 1:
|
||||||
|
area_id = str(int(time.time()))
|
||||||
|
data["areas"] = [
|
||||||
|
attr.asdict(
|
||||||
|
AreaEntry(
|
||||||
|
**{
|
||||||
|
"name": "Alarmo",
|
||||||
|
"modes": {
|
||||||
|
mode: attr.asdict(
|
||||||
|
ModeEntry(
|
||||||
|
enabled=bool(config["enabled"]),
|
||||||
|
exit_time=int(config["leave_time"] or 0),
|
||||||
|
entry_time=int(config["entry_time"] or 0),
|
||||||
|
trigger_time=int(
|
||||||
|
data["config"]["trigger_time"] or 0
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for (mode, config) in data["config"]["modes"].items()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
area_id=area_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
if "sensors" in data:
|
||||||
|
for sensor in data["sensors"]:
|
||||||
|
sensor["area"] = area_id
|
||||||
|
|
||||||
|
if old_major_version <= 3:
|
||||||
|
data["sensors"] = [
|
||||||
|
attr.asdict(
|
||||||
|
SensorEntry(
|
||||||
|
**{
|
||||||
|
**omit(sensor, ["immediate", "name"]),
|
||||||
|
"use_exit_delay": not sensor["immediate"]
|
||||||
|
and not sensor["always_on"],
|
||||||
|
"use_entry_delay": not sensor["immediate"]
|
||||||
|
and not sensor["always_on"],
|
||||||
|
"auto_bypass_modes": sensor["modes"]
|
||||||
|
if sensor.get("auto_bypass")
|
||||||
|
else [],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for sensor in data["sensors"]
|
||||||
|
]
|
||||||
|
|
||||||
|
if old_major_version <= 4:
|
||||||
|
data["sensors"] = [
|
||||||
|
attr.asdict(
|
||||||
|
SensorEntry(
|
||||||
|
**omit(sensor, ["name"]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for sensor in data["sensors"]
|
||||||
|
]
|
||||||
|
|
||||||
|
data["automations"] = [
|
||||||
|
migrate_automation(automation) for automation in data["automations"]
|
||||||
|
]
|
||||||
|
|
||||||
|
if old_major_version <= 5 or (old_major_version == 6 and old_minor_version < 2):
|
||||||
|
data["config"] = attr.asdict(
|
||||||
|
Config(
|
||||||
|
**omit(data["config"], ["code_mode_change_required"]),
|
||||||
|
code_mode_change_required=data["config"]["code_arm_required"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if old_major_version <= 5 or (old_major_version == 6 and old_minor_version < 3):
|
||||||
|
data["sensor_groups"] = [
|
||||||
|
attr.asdict(
|
||||||
|
SensorGroupEntry(
|
||||||
|
**{
|
||||||
|
**omit(sensorGroup, ["entities"]),
|
||||||
|
"entities": list(set(sensorGroup["entities"])),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for sensorGroup in data["sensor_groups"]
|
||||||
|
]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoStorage:
|
||||||
|
"""Class to hold alarmo configuration data."""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant) -> None:
|
||||||
|
"""Initialize the storage."""
|
||||||
|
self.hass = hass
|
||||||
|
self.config: Config = Config()
|
||||||
|
self.areas: MutableMapping[str, AreaEntry] = {}
|
||||||
|
self.sensors: MutableMapping[str, SensorEntry] = {}
|
||||||
|
self.users: MutableMapping[str, UserEntry] = {}
|
||||||
|
self.automations: MutableMapping[str, AutomationEntry] = {}
|
||||||
|
self.sensor_groups: MutableMapping[str, SensorGroupEntry] = {}
|
||||||
|
self._store = MigratableStore(
|
||||||
|
hass,
|
||||||
|
STORAGE_VERSION_MAJOR,
|
||||||
|
STORAGE_KEY,
|
||||||
|
minor_version=STORAGE_VERSION_MINOR,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_load(self) -> None: # noqa: PLR0912
|
||||||
|
"""Load the registry of schedule entries."""
|
||||||
|
data = await self._store.async_load()
|
||||||
|
config: Config = Config()
|
||||||
|
areas: OrderedDict[str, AreaEntry] = OrderedDict()
|
||||||
|
sensors: OrderedDict[str, SensorEntry] = OrderedDict()
|
||||||
|
users: OrderedDict[str, UserEntry] = OrderedDict()
|
||||||
|
automations: OrderedDict[str, AutomationEntry] = OrderedDict()
|
||||||
|
sensor_groups: OrderedDict[str, SensorGroupEntry] = OrderedDict()
|
||||||
|
|
||||||
|
if data is not None:
|
||||||
|
config = Config(
|
||||||
|
code_arm_required=data["config"]["code_arm_required"],
|
||||||
|
code_mode_change_required=data["config"]["code_mode_change_required"],
|
||||||
|
code_disarm_required=data["config"]["code_disarm_required"],
|
||||||
|
code_format=data["config"]["code_format"],
|
||||||
|
disarm_after_trigger=data["config"]["disarm_after_trigger"],
|
||||||
|
ignore_blocking_sensors_after_trigger=data["config"].get(
|
||||||
|
"ignore_blocking_sensors_after_trigger", False
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if "mqtt" in data["config"]:
|
||||||
|
config = attr.evolve(
|
||||||
|
config,
|
||||||
|
**{
|
||||||
|
"mqtt": MqttConfig(**data["config"]["mqtt"]),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if "master" in data["config"]:
|
||||||
|
config = attr.evolve(
|
||||||
|
config,
|
||||||
|
**{
|
||||||
|
"master": MasterConfig(**data["config"]["master"]),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if "areas" in data:
|
||||||
|
for area in data["areas"]:
|
||||||
|
modes = {
|
||||||
|
mode: ModeEntry(
|
||||||
|
enabled=config["enabled"],
|
||||||
|
exit_time=config["exit_time"],
|
||||||
|
entry_time=config["entry_time"],
|
||||||
|
trigger_time=config["trigger_time"],
|
||||||
|
)
|
||||||
|
for (mode, config) in area["modes"].items()
|
||||||
|
}
|
||||||
|
areas[area["area_id"]] = AreaEntry(
|
||||||
|
area_id=area["area_id"], name=area["name"], modes=modes
|
||||||
|
)
|
||||||
|
|
||||||
|
if "sensors" in data:
|
||||||
|
for sensor in data["sensors"]:
|
||||||
|
sensors[sensor["entity_id"]] = SensorEntry(**sensor)
|
||||||
|
|
||||||
|
if "users" in data:
|
||||||
|
for user in data["users"]:
|
||||||
|
users[user["user_id"]] = UserEntry(**omit(user, ["is_admin"]))
|
||||||
|
|
||||||
|
if "automations" in data:
|
||||||
|
for automation in data["automations"]:
|
||||||
|
automations[automation["automation_id"]] = AutomationEntry(
|
||||||
|
**parse_automation_entry(automation)
|
||||||
|
)
|
||||||
|
|
||||||
|
if "sensor_groups" in data:
|
||||||
|
for group in data["sensor_groups"]:
|
||||||
|
sensor_groups[group["group_id"]] = SensorGroupEntry(**group)
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
self.areas = areas
|
||||||
|
self.sensors = sensors
|
||||||
|
self.automations = automations
|
||||||
|
self.users = users
|
||||||
|
self.sensor_groups = sensor_groups
|
||||||
|
|
||||||
|
if not areas:
|
||||||
|
await self.async_factory_default()
|
||||||
|
|
||||||
|
async def async_factory_default(self):
|
||||||
|
"""Reset to factory default configuration."""
|
||||||
|
self.async_create_area(
|
||||||
|
{
|
||||||
|
"name": "Alarmo",
|
||||||
|
"modes": {
|
||||||
|
const.CONF_ALARM_ARMED_AWAY: attr.asdict(
|
||||||
|
ModeEntry(
|
||||||
|
enabled=True, exit_time=60, entry_time=60, trigger_time=1800
|
||||||
|
)
|
||||||
|
),
|
||||||
|
const.CONF_ALARM_ARMED_HOME: attr.asdict(
|
||||||
|
ModeEntry(enabled=True, trigger_time=1800)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_schedule_save(self) -> None:
|
||||||
|
"""Schedule saving the registry of alarmo."""
|
||||||
|
self._store.async_delay_save(self._data_to_save, SAVE_DELAY)
|
||||||
|
|
||||||
|
async def async_save(self) -> None:
|
||||||
|
"""Save the registry of alarmo."""
|
||||||
|
await self._store.async_save(self._data_to_save())
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _data_to_save(self) -> dict:
|
||||||
|
"""Return data for the registry for alarmo to store in a file."""
|
||||||
|
store_data = {
|
||||||
|
"config": attr.asdict(self.config),
|
||||||
|
}
|
||||||
|
|
||||||
|
store_data["areas"] = [attr.asdict(entry) for entry in self.areas.values()]
|
||||||
|
store_data["sensors"] = [attr.asdict(entry) for entry in self.sensors.values()]
|
||||||
|
store_data["users"] = [attr.asdict(entry) for entry in self.users.values()]
|
||||||
|
store_data["automations"] = [
|
||||||
|
attr.asdict(entry) for entry in self.automations.values()
|
||||||
|
]
|
||||||
|
store_data["sensor_groups"] = [
|
||||||
|
attr.asdict(entry) for entry in self.sensor_groups.values()
|
||||||
|
]
|
||||||
|
|
||||||
|
return store_data
|
||||||
|
|
||||||
|
async def async_delete(self):
|
||||||
|
"""Delete config."""
|
||||||
|
_LOGGER.warning("Removing alarmo configuration data!")
|
||||||
|
await self._store.async_remove()
|
||||||
|
self.config = Config()
|
||||||
|
self.areas = {}
|
||||||
|
self.sensors = {}
|
||||||
|
self.users = {}
|
||||||
|
self.automations = {}
|
||||||
|
self.sensor_groups = {}
|
||||||
|
await self.async_factory_default()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_config(self):
|
||||||
|
"""Get current config."""
|
||||||
|
return attr.asdict(self.config)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_config(self, changes: dict):
|
||||||
|
"""Update existing config."""
|
||||||
|
old = self.config
|
||||||
|
new = self.config = attr.evolve(old, **changes)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return attr.asdict(new)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_mode_config(self, mode: str, changes: dict):
|
||||||
|
"""Update existing config."""
|
||||||
|
modes = self.config.modes
|
||||||
|
old = self.config.modes[mode] if mode in self.config.modes else ModeEntry()
|
||||||
|
new = attr.evolve(old, **changes)
|
||||||
|
modes[mode] = new
|
||||||
|
self.config = attr.evolve(self.config, **{"modes": modes})
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_area(self, area_id) -> AreaEntry:
|
||||||
|
"""Get an existing AreaEntry by id."""
|
||||||
|
res = self.areas.get(area_id)
|
||||||
|
return attr.asdict(res) if res else None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_areas(self):
|
||||||
|
"""Get an existing AreaEntry by id."""
|
||||||
|
res = {}
|
||||||
|
for key, val in self.areas.items():
|
||||||
|
res[key] = attr.asdict(val)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_create_area(self, data: dict) -> AreaEntry:
|
||||||
|
"""Create a new AreaEntry."""
|
||||||
|
area_id = str(int(time.time()))
|
||||||
|
new_area = AreaEntry(**data, area_id=area_id)
|
||||||
|
self.areas[area_id] = new_area
|
||||||
|
self.async_schedule_save()
|
||||||
|
return attr.asdict(new_area)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_delete_area(self, area_id: str) -> None:
|
||||||
|
"""Delete AreaEntry."""
|
||||||
|
if area_id in self.areas:
|
||||||
|
del self.areas[area_id]
|
||||||
|
self.async_schedule_save()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_area(self, area_id: str, changes: dict) -> AreaEntry:
|
||||||
|
"""Update existing self."""
|
||||||
|
old = self.areas[area_id]
|
||||||
|
new = self.areas[area_id] = attr.evolve(old, **changes)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return attr.asdict(new)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_sensor(self, entity_id) -> SensorEntry:
|
||||||
|
"""Get an existing SensorEntry by id."""
|
||||||
|
res = self.sensors.get(entity_id)
|
||||||
|
return attr.asdict(res) if res else None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_sensors(self):
|
||||||
|
"""Get an existing SensorEntry by id."""
|
||||||
|
res = {}
|
||||||
|
for key, val in self.sensors.items():
|
||||||
|
res[key] = attr.asdict(val)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_create_sensor(self, entity_id: str, data: dict) -> SensorEntry:
|
||||||
|
"""Create a new SensorEntry."""
|
||||||
|
if entity_id in self.sensors:
|
||||||
|
return False
|
||||||
|
new_sensor = SensorEntry(**data, entity_id=entity_id)
|
||||||
|
self.sensors[entity_id] = new_sensor
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new_sensor
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_delete_sensor(self, entity_id: str) -> None:
|
||||||
|
"""Delete SensorEntry."""
|
||||||
|
if entity_id in self.sensors:
|
||||||
|
del self.sensors[entity_id]
|
||||||
|
self.async_schedule_save()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_sensor(self, entity_id: str, changes: dict) -> SensorEntry:
|
||||||
|
"""Update existing SensorEntry."""
|
||||||
|
old = self.sensors[entity_id]
|
||||||
|
new = self.sensors[entity_id] = attr.evolve(old, **changes)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_user(self, user_id) -> UserEntry:
|
||||||
|
"""Get an existing UserEntry by id."""
|
||||||
|
res = self.users.get(user_id)
|
||||||
|
return attr.asdict(res) if res else None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_users(self):
|
||||||
|
"""Get an existing UserEntry by id."""
|
||||||
|
res = {}
|
||||||
|
for key, val in self.users.items():
|
||||||
|
res[key] = attr.asdict(val)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_create_user(self, data: dict) -> UserEntry:
|
||||||
|
"""Create a new UserEntry."""
|
||||||
|
user_id = str(int(time.time()))
|
||||||
|
new_user = UserEntry(**data, user_id=user_id)
|
||||||
|
self.users[user_id] = new_user
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new_user
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_delete_user(self, user_id: str) -> None:
|
||||||
|
"""Delete UserEntry."""
|
||||||
|
if user_id in self.users:
|
||||||
|
del self.users[user_id]
|
||||||
|
self.async_schedule_save()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_user(self, user_id: str, changes: dict) -> UserEntry:
|
||||||
|
"""Update existing UserEntry."""
|
||||||
|
old = self.users[user_id]
|
||||||
|
new = self.users[user_id] = attr.evolve(old, **changes)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_automations(self):
|
||||||
|
"""Get an existing AutomationEntry by id."""
|
||||||
|
res = {}
|
||||||
|
for key, val in self.automations.items():
|
||||||
|
res[key] = attr.asdict(val)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_create_automation(self, data: dict) -> AutomationEntry:
|
||||||
|
"""Create a new AutomationEntry."""
|
||||||
|
automation_id = str(int(time.time()))
|
||||||
|
new_automation = AutomationEntry(
|
||||||
|
**parse_automation_entry(data), automation_id=automation_id
|
||||||
|
)
|
||||||
|
self.automations[automation_id] = new_automation
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new_automation
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_delete_automation(self, automation_id: str) -> None:
|
||||||
|
"""Delete AutomationEntry."""
|
||||||
|
if automation_id in self.automations:
|
||||||
|
del self.automations[automation_id]
|
||||||
|
self.async_schedule_save()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_automation(
|
||||||
|
self, automation_id: str, changes: dict
|
||||||
|
) -> AutomationEntry:
|
||||||
|
"""Update existing AutomationEntry."""
|
||||||
|
old = self.automations[automation_id]
|
||||||
|
new = self.automations[automation_id] = attr.evolve(
|
||||||
|
old, **parse_automation_entry(changes)
|
||||||
|
)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_sensor_group(self, group_id) -> SensorGroupEntry:
|
||||||
|
"""Get an existing SensorGroupEntry by id."""
|
||||||
|
res = self.sensor_groups.get(group_id)
|
||||||
|
return attr.asdict(res) if res else None
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_sensor_groups(self):
|
||||||
|
"""Get an existing SensorGroupEntry by id."""
|
||||||
|
res = {}
|
||||||
|
for key, val in self.sensor_groups.items():
|
||||||
|
res[key] = attr.asdict(val)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_create_sensor_group(self, data: dict) -> SensorGroupEntry:
|
||||||
|
"""Create a new SensorGroupEntry."""
|
||||||
|
group_id = str(int(time.time()))
|
||||||
|
new_group = SensorGroupEntry(**data, group_id=group_id)
|
||||||
|
self.sensor_groups[group_id] = new_group
|
||||||
|
self.async_schedule_save()
|
||||||
|
return group_id
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_delete_sensor_group(self, group_id: str) -> None:
|
||||||
|
"""Delete SensorGroupEntry."""
|
||||||
|
if group_id in self.sensor_groups:
|
||||||
|
del self.sensor_groups[group_id]
|
||||||
|
self.async_schedule_save()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_sensor_group(
|
||||||
|
self, group_id: str, changes: dict
|
||||||
|
) -> SensorGroupEntry:
|
||||||
|
"""Update existing SensorGroupEntry."""
|
||||||
|
old = self.sensor_groups[group_id]
|
||||||
|
new = self.sensor_groups[group_id] = attr.evolve(old, **changes)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new
|
||||||
|
|
||||||
|
|
||||||
|
async def async_get_registry(hass: HomeAssistant) -> AlarmoStorage:
|
||||||
|
"""Return alarmo storage instance."""
|
||||||
|
task = hass.data.get(DATA_REGISTRY)
|
||||||
|
|
||||||
|
if task is None:
|
||||||
|
|
||||||
|
async def _load_reg() -> AlarmoStorage:
|
||||||
|
registry = AlarmoStorage(hass)
|
||||||
|
await registry.async_load()
|
||||||
|
return registry
|
||||||
|
|
||||||
|
task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg())
|
||||||
|
|
||||||
|
return cast(AlarmoStorage, await task)
|
||||||
594
custom_components/alarmo/websockets.py
Normal file
594
custom_components/alarmo/websockets.py
Normal file
@@ -0,0 +1,594 @@
|
|||||||
|
"""WebSocket handler and registration for Alarmo configuration management."""
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_CODE,
|
||||||
|
ATTR_NAME,
|
||||||
|
ATTR_STATE,
|
||||||
|
ATTR_SERVICE,
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
ATTR_CODE_FORMAT,
|
||||||
|
CONF_SERVICE_DATA,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.components import websocket_api
|
||||||
|
from homeassistant.components.http import HomeAssistantView
|
||||||
|
from homeassistant.components.mqtt import (
|
||||||
|
DOMAIN as ATTR_MQTT,
|
||||||
|
)
|
||||||
|
from homeassistant.components.mqtt import (
|
||||||
|
CONF_STATE_TOPIC,
|
||||||
|
CONF_COMMAND_TOPIC,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
async_dispatcher_send,
|
||||||
|
async_dispatcher_connect,
|
||||||
|
)
|
||||||
|
from homeassistant.components.websocket_api import decorators, async_register_command
|
||||||
|
from homeassistant.components.alarm_control_panel import (
|
||||||
|
ATTR_CODE_ARM_REQUIRED,
|
||||||
|
CodeFormat,
|
||||||
|
)
|
||||||
|
from homeassistant.components.http.data_validator import RequestDataValidator
|
||||||
|
|
||||||
|
from . import const
|
||||||
|
from .mqtt import (
|
||||||
|
CONF_EVENT_TOPIC,
|
||||||
|
)
|
||||||
|
from .sensors import (
|
||||||
|
ATTR_GROUP,
|
||||||
|
ATTR_TIMEOUT,
|
||||||
|
SENSOR_TYPES,
|
||||||
|
ATTR_ENTITIES,
|
||||||
|
ATTR_GROUP_ID,
|
||||||
|
ATTR_ALWAYS_ON,
|
||||||
|
ATTR_ALLOW_OPEN,
|
||||||
|
ATTR_AUTO_BYPASS,
|
||||||
|
ATTR_ENTRY_DELAY,
|
||||||
|
ATTR_EVENT_COUNT,
|
||||||
|
ATTR_ARM_ON_CLOSE,
|
||||||
|
ATTR_NEW_ENTITY_ID,
|
||||||
|
ATTR_USE_EXIT_DELAY,
|
||||||
|
ATTR_USE_ENTRY_DELAY,
|
||||||
|
ATTR_AUTO_BYPASS_MODES,
|
||||||
|
ATTR_TRIGGER_UNAVAILABLE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
@decorators.websocket_command(
|
||||||
|
{
|
||||||
|
vol.Required("type"): "alarmo_config_updated",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@decorators.async_response
|
||||||
|
async def handle_subscribe_updates(hass, connection, msg):
|
||||||
|
"""Handle subscribe updates."""
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_handle_event():
|
||||||
|
"""Forward events to websocket."""
|
||||||
|
connection.send_message(
|
||||||
|
{
|
||||||
|
"id": msg["id"],
|
||||||
|
"type": "event",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
connection.subscriptions[msg["id"]] = async_dispatcher_connect(
|
||||||
|
hass, "alarmo_update_frontend", async_handle_event
|
||||||
|
)
|
||||||
|
connection.send_result(msg["id"])
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoConfigView(HomeAssistantView):
|
||||||
|
"""Login to Home Assistant cloud."""
|
||||||
|
|
||||||
|
url = "/api/alarmo/config"
|
||||||
|
name = "api:alarmo:config"
|
||||||
|
|
||||||
|
@RequestDataValidator(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(ATTR_CODE_ARM_REQUIRED): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_CODE_DISARM_REQUIRED): cv.boolean,
|
||||||
|
vol.Optional(
|
||||||
|
const.ATTR_IGNORE_BLOCKING_SENSORS_AFTER_TRIGGER
|
||||||
|
): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_CODE_MODE_CHANGE_REQUIRED): cv.boolean,
|
||||||
|
vol.Optional(ATTR_CODE_FORMAT): vol.In(
|
||||||
|
[CodeFormat.NUMBER, CodeFormat.TEXT]
|
||||||
|
),
|
||||||
|
vol.Optional(const.ATTR_TRIGGER_TIME): cv.positive_int,
|
||||||
|
vol.Optional(const.ATTR_DISARM_AFTER_TRIGGER): cv.boolean,
|
||||||
|
vol.Optional(ATTR_MQTT): vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(const.ATTR_ENABLED): cv.boolean,
|
||||||
|
vol.Required(CONF_STATE_TOPIC): cv.string,
|
||||||
|
vol.Optional(const.ATTR_STATE_PAYLOAD): vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(const.CONF_ALARM_DISARMED): cv.string,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_HOME): cv.string,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_AWAY): cv.string,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_NIGHT): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
const.CONF_ALARM_ARMED_CUSTOM_BYPASS
|
||||||
|
): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
const.CONF_ALARM_ARMED_VACATION
|
||||||
|
): cv.string,
|
||||||
|
vol.Optional(const.CONF_ALARM_PENDING): cv.string,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMING): cv.string,
|
||||||
|
vol.Optional(const.CONF_ALARM_TRIGGERED): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
vol.Required(CONF_COMMAND_TOPIC): cv.string,
|
||||||
|
vol.Optional(const.ATTR_COMMAND_PAYLOAD): vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(const.COMMAND_ARM_AWAY): cv.string,
|
||||||
|
vol.Optional(const.COMMAND_ARM_HOME): cv.string,
|
||||||
|
vol.Optional(const.COMMAND_ARM_NIGHT): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
const.COMMAND_ARM_CUSTOM_BYPASS
|
||||||
|
): cv.string,
|
||||||
|
vol.Optional(const.COMMAND_ARM_VACATION): cv.string,
|
||||||
|
vol.Optional(const.COMMAND_DISARM): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
vol.Required(const.ATTR_REQUIRE_CODE): cv.boolean,
|
||||||
|
vol.Required(CONF_EVENT_TOPIC): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
vol.Optional(const.ATTR_MASTER): vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(const.ATTR_ENABLED): cv.boolean,
|
||||||
|
vol.Optional(ATTR_NAME): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async def post(self, request, data):
|
||||||
|
"""Handle config update request."""
|
||||||
|
hass = request.app["hass"]
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
await coordinator.async_update_config(data)
|
||||||
|
async_dispatcher_send(hass, "alarmo_update_frontend")
|
||||||
|
return self.json({"success": True})
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoAreaView(HomeAssistantView):
|
||||||
|
"""Login to Home Assistant cloud."""
|
||||||
|
|
||||||
|
url = "/api/alarmo/area"
|
||||||
|
name = "api:alarmo:area"
|
||||||
|
|
||||||
|
mode_schema = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(const.ATTR_ENABLED): cv.boolean,
|
||||||
|
vol.Required(const.ATTR_EXIT_TIME): vol.Any(cv.positive_int, None),
|
||||||
|
vol.Required(const.ATTR_ENTRY_TIME): vol.Any(cv.positive_int, None),
|
||||||
|
vol.Optional(const.ATTR_TRIGGER_TIME): vol.Any(cv.positive_int, None),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@RequestDataValidator(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional("area_id"): cv.string,
|
||||||
|
vol.Optional(ATTR_NAME): cv.string,
|
||||||
|
vol.Optional(const.ATTR_REMOVE): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_MODES): vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_AWAY): mode_schema,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_HOME): mode_schema,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_NIGHT): mode_schema,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_CUSTOM_BYPASS): mode_schema,
|
||||||
|
vol.Optional(const.CONF_ALARM_ARMED_VACATION): mode_schema,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async def post(self, request, data):
|
||||||
|
"""Handle config update request."""
|
||||||
|
hass = request.app["hass"]
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
if "area_id" in data:
|
||||||
|
area = data["area_id"]
|
||||||
|
del data["area_id"]
|
||||||
|
else:
|
||||||
|
area = None
|
||||||
|
await coordinator.async_update_area_config(area, data)
|
||||||
|
async_dispatcher_send(hass, "alarmo_update_frontend")
|
||||||
|
return self.json({"success": True})
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoSensorView(HomeAssistantView):
|
||||||
|
"""Login to Home Assistant cloud."""
|
||||||
|
|
||||||
|
url = "/api/alarmo/sensors"
|
||||||
|
name = "api:alarmo:sensors"
|
||||||
|
|
||||||
|
@RequestDataValidator(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
|
||||||
|
vol.Optional(const.ATTR_REMOVE): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_TYPE): vol.In(SENSOR_TYPES),
|
||||||
|
vol.Optional(const.ATTR_MODES): vol.All(
|
||||||
|
cv.ensure_list, [vol.In(const.ARM_MODES)]
|
||||||
|
),
|
||||||
|
vol.Optional(ATTR_USE_EXIT_DELAY): cv.boolean,
|
||||||
|
vol.Optional(ATTR_USE_ENTRY_DELAY): cv.boolean,
|
||||||
|
vol.Optional(ATTR_ARM_ON_CLOSE): cv.boolean,
|
||||||
|
vol.Optional(ATTR_ALLOW_OPEN): cv.boolean,
|
||||||
|
vol.Optional(ATTR_ALWAYS_ON): cv.boolean,
|
||||||
|
vol.Optional(ATTR_TRIGGER_UNAVAILABLE): cv.boolean,
|
||||||
|
vol.Optional(ATTR_AUTO_BYPASS): cv.boolean,
|
||||||
|
vol.Optional(ATTR_AUTO_BYPASS_MODES): vol.All(
|
||||||
|
cv.ensure_list, [vol.In(const.ARM_MODES)]
|
||||||
|
),
|
||||||
|
vol.Optional(const.ATTR_AREA): cv.string,
|
||||||
|
vol.Optional(const.ATTR_ENABLED): cv.boolean,
|
||||||
|
vol.Optional(ATTR_GROUP): vol.Any(cv.string, None),
|
||||||
|
vol.Optional(ATTR_ENTRY_DELAY): vol.Any(cv.positive_int, None),
|
||||||
|
vol.Optional(ATTR_NEW_ENTITY_ID): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async def post(self, request, data):
|
||||||
|
"""Handle config update request."""
|
||||||
|
hass = request.app["hass"]
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
entity = data[ATTR_ENTITY_ID]
|
||||||
|
del data[ATTR_ENTITY_ID]
|
||||||
|
coordinator.async_update_sensor_config(entity, data)
|
||||||
|
async_dispatcher_send(hass, "alarmo_update_frontend")
|
||||||
|
return self.json({"success": True})
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoUserView(HomeAssistantView):
|
||||||
|
"""Login to Home Assistant cloud."""
|
||||||
|
|
||||||
|
url = "/api/alarmo/users"
|
||||||
|
name = "api:alarmo:users"
|
||||||
|
|
||||||
|
@RequestDataValidator(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(const.ATTR_USER_ID): cv.string,
|
||||||
|
vol.Optional(const.ATTR_REMOVE): cv.boolean,
|
||||||
|
vol.Optional(ATTR_NAME): cv.string,
|
||||||
|
vol.Optional(const.ATTR_ENABLED): cv.boolean,
|
||||||
|
vol.Optional(ATTR_CODE): cv.string,
|
||||||
|
vol.Optional(const.ATTR_OLD_CODE): cv.string,
|
||||||
|
vol.Optional(const.ATTR_CAN_ARM): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_CAN_DISARM): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_IS_OVERRIDE_CODE): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_AREA_LIMIT): vol.All(
|
||||||
|
cv.ensure_list, [cv.string]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async def post(self, request, data):
|
||||||
|
"""Handle config update request."""
|
||||||
|
hass = request.app["hass"]
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
user_id = None
|
||||||
|
if const.ATTR_USER_ID in data:
|
||||||
|
user_id = data[const.ATTR_USER_ID]
|
||||||
|
del data[const.ATTR_USER_ID]
|
||||||
|
err = coordinator.async_update_user_config(user_id, data)
|
||||||
|
async_dispatcher_send(hass, "alarmo_update_frontend")
|
||||||
|
return self.json({"success": not isinstance(err, str), "error": err})
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoAutomationView(HomeAssistantView):
|
||||||
|
"""Login to Home Assistant cloud."""
|
||||||
|
|
||||||
|
url = "/api/alarmo/automations"
|
||||||
|
name = "api:alarmo:automations"
|
||||||
|
|
||||||
|
@RequestDataValidator(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(const.ATTR_AUTOMATION_ID): cv.string,
|
||||||
|
vol.Optional(ATTR_NAME): cv.string,
|
||||||
|
vol.Optional(const.ATTR_TYPE): cv.string,
|
||||||
|
vol.Optional(const.ATTR_TRIGGERS): vol.All(
|
||||||
|
cv.ensure_list,
|
||||||
|
[
|
||||||
|
vol.Any(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(const.ATTR_EVENT): cv.string,
|
||||||
|
vol.Optional(const.ATTR_AREA): vol.Any(
|
||||||
|
int,
|
||||||
|
cv.string,
|
||||||
|
),
|
||||||
|
vol.Optional(const.ATTR_MODES): vol.All(
|
||||||
|
cv.ensure_list, [vol.In(const.ARM_MODES)]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.string,
|
||||||
|
vol.Required(ATTR_STATE): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
vol.Optional(const.ATTR_ACTIONS): vol.All(
|
||||||
|
cv.ensure_list,
|
||||||
|
[
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.string,
|
||||||
|
vol.Required(ATTR_SERVICE): cv.string,
|
||||||
|
vol.Optional(CONF_SERVICE_DATA): dict,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
vol.Optional(const.ATTR_ENABLED): cv.boolean,
|
||||||
|
vol.Optional(const.ATTR_REMOVE): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async def post(self, request, data):
|
||||||
|
"""Handle config update request."""
|
||||||
|
hass = request.app["hass"]
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
automation_id = None
|
||||||
|
if const.ATTR_AUTOMATION_ID in data:
|
||||||
|
automation_id = data[const.ATTR_AUTOMATION_ID]
|
||||||
|
del data[const.ATTR_AUTOMATION_ID]
|
||||||
|
coordinator.async_update_automation_config(automation_id, data)
|
||||||
|
async_dispatcher_send(hass, "alarmo_update_frontend")
|
||||||
|
return self.json({"success": True})
|
||||||
|
|
||||||
|
|
||||||
|
class AlarmoSensorGroupView(HomeAssistantView):
|
||||||
|
"""Login to Home Assistant cloud."""
|
||||||
|
|
||||||
|
url = "/api/alarmo/sensor_groups"
|
||||||
|
name = "api:alarmo:sensor_groups"
|
||||||
|
|
||||||
|
@RequestDataValidator(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(ATTR_GROUP_ID): cv.string,
|
||||||
|
vol.Optional(ATTR_NAME): cv.string,
|
||||||
|
vol.Optional(ATTR_ENTITIES): vol.All(
|
||||||
|
cv.ensure_list, vol.Unique(), [cv.string]
|
||||||
|
),
|
||||||
|
vol.Optional(ATTR_TIMEOUT): cv.positive_int,
|
||||||
|
vol.Optional(ATTR_EVENT_COUNT): cv.positive_int,
|
||||||
|
vol.Optional(const.ATTR_REMOVE): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
async def post(self, request, data):
|
||||||
|
"""Handle config update request."""
|
||||||
|
hass = request.app["hass"]
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
group_id = None
|
||||||
|
if ATTR_GROUP_ID in data:
|
||||||
|
group_id = data[ATTR_GROUP_ID]
|
||||||
|
del data[ATTR_GROUP_ID]
|
||||||
|
coordinator.async_update_sensor_group_config(group_id, data)
|
||||||
|
async_dispatcher_send(hass, "alarmo_update_frontend")
|
||||||
|
return self.json({"success": True})
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_config(hass, connection, msg):
|
||||||
|
"""Publish config data."""
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
config = coordinator.store.async_get_config()
|
||||||
|
connection.send_result(msg["id"], config)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_areas(hass, connection, msg):
|
||||||
|
"""Publish area data."""
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
areas = coordinator.store.async_get_areas()
|
||||||
|
connection.send_result(msg["id"], areas)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_sensors(hass, connection, msg):
|
||||||
|
"""Publish sensor data."""
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
sensors = coordinator.store.async_get_sensors()
|
||||||
|
for entity_id in sensors.keys():
|
||||||
|
group = coordinator.async_get_group_for_sensor(entity_id)
|
||||||
|
sensors[entity_id]["group"] = group
|
||||||
|
connection.send_result(msg["id"], sensors)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_users(hass, connection, msg):
|
||||||
|
"""Publish user data."""
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
users = coordinator.store.async_get_users()
|
||||||
|
connection.send_result(msg["id"], users)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_automations(hass, connection, msg):
|
||||||
|
"""Publish automations data."""
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
automations = coordinator.store.async_get_automations()
|
||||||
|
connection.send_result(msg["id"], automations)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_alarm_entities(hass, connection, msg):
|
||||||
|
"""Publish alarm entity data."""
|
||||||
|
result = [
|
||||||
|
{"entity_id": entity.entity_id, "area_id": area_id}
|
||||||
|
for (area_id, entity) in hass.data[const.DOMAIN]["areas"].items()
|
||||||
|
]
|
||||||
|
if hass.data[const.DOMAIN]["master"]:
|
||||||
|
result.append(
|
||||||
|
{"entity_id": hass.data[const.DOMAIN]["master"].entity_id, "area_id": 0}
|
||||||
|
)
|
||||||
|
connection.send_result(msg["id"], result)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_sensor_groups(hass, connection, msg):
|
||||||
|
"""Publish sensor_group data."""
|
||||||
|
coordinator = hass.data[const.DOMAIN]["coordinator"]
|
||||||
|
groups = coordinator.store.async_get_sensor_groups()
|
||||||
|
connection.send_result(msg["id"], groups)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_countdown(hass, connection, msg):
|
||||||
|
"""Publish countdown time for alarm entity."""
|
||||||
|
entity_id = msg["entity_id"]
|
||||||
|
item = next(
|
||||||
|
(
|
||||||
|
entity
|
||||||
|
for entity in hass.data[const.DOMAIN]["areas"].values()
|
||||||
|
if entity.entity_id == entity_id
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
hass.data[const.DOMAIN]["master"]
|
||||||
|
and not item
|
||||||
|
and hass.data[const.DOMAIN]["master"].entity_id == entity_id
|
||||||
|
):
|
||||||
|
item = hass.data[const.DOMAIN]["master"]
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"delay": item.delay if item else 0,
|
||||||
|
"remaining": round((item.expiration - dt_util.utcnow()).total_seconds(), 2)
|
||||||
|
if item and item.expiration
|
||||||
|
else 0,
|
||||||
|
}
|
||||||
|
connection.send_result(msg["id"], data)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def websocket_get_ready_to_arm_modes(hass, connection, msg):
|
||||||
|
"""Publish ready_to_arm_modes for alarm entity."""
|
||||||
|
entity_id = msg["entity_id"]
|
||||||
|
item = next(
|
||||||
|
(
|
||||||
|
entity
|
||||||
|
for entity in hass.data[const.DOMAIN]["areas"].values()
|
||||||
|
if entity.entity_id == entity_id
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
hass.data[const.DOMAIN]["master"]
|
||||||
|
and not item
|
||||||
|
and hass.data[const.DOMAIN]["master"].entity_id == entity_id
|
||||||
|
):
|
||||||
|
item = hass.data[const.DOMAIN]["master"]
|
||||||
|
|
||||||
|
data = {"modes": item._ready_to_arm_modes if item else None}
|
||||||
|
connection.send_result(msg["id"], data)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_register_websockets(hass):
|
||||||
|
"""Register websocket handlers."""
|
||||||
|
hass.http.register_view(AlarmoConfigView)
|
||||||
|
hass.http.register_view(AlarmoSensorView)
|
||||||
|
hass.http.register_view(AlarmoUserView)
|
||||||
|
hass.http.register_view(AlarmoAutomationView)
|
||||||
|
hass.http.register_view(AlarmoAreaView)
|
||||||
|
hass.http.register_view(AlarmoSensorGroupView)
|
||||||
|
|
||||||
|
async_register_command(hass, handle_subscribe_updates)
|
||||||
|
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/config",
|
||||||
|
websocket_get_config,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{vol.Required("type"): "alarmo/config"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/areas",
|
||||||
|
websocket_get_areas,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{vol.Required("type"): "alarmo/areas"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/sensors",
|
||||||
|
websocket_get_sensors,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{vol.Required("type"): "alarmo/sensors"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/users",
|
||||||
|
websocket_get_users,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{vol.Required("type"): "alarmo/users"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/automations",
|
||||||
|
websocket_get_automations,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{vol.Required("type"): "alarmo/automations"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/entities",
|
||||||
|
websocket_get_alarm_entities,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{vol.Required("type"): "alarmo/entities"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/sensor_groups",
|
||||||
|
websocket_get_sensor_groups,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{vol.Required("type"): "alarmo/sensor_groups"}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/countdown",
|
||||||
|
websocket_get_countdown,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
vol.Required("type"): "alarmo/countdown",
|
||||||
|
vol.Required("entity_id"): cv.entity_id,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async_register_command(
|
||||||
|
hass,
|
||||||
|
"alarmo/ready_to_arm_modes",
|
||||||
|
websocket_get_ready_to_arm_modes,
|
||||||
|
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
vol.Required("type"): "alarmo/ready_to_arm_modes",
|
||||||
|
vol.Required("entity_id"): cv.entity_id,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user