| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | // Copyright (C) 2022 Jon Beniston, M7RCE                                        //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is free software; you can redistribute it and/or modify          //
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by          //
 | 
					
						
							|  |  |  | // the Free Software Foundation as version 3 of the License, or                  //
 | 
					
						
							|  |  |  | // (at your option) any later version.                                           //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,               //
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
					
						
							|  |  |  | // GNU General Public License V3 for more details.                               //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License             //
 | 
					
						
							|  |  |  | // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <QDebug>
 | 
					
						
							|  |  |  | #include <QUrl>
 | 
					
						
							|  |  |  | #include <QUrlQuery>
 | 
					
						
							|  |  |  | #include <QNetworkReply>
 | 
					
						
							|  |  |  | #include <QJsonDocument>
 | 
					
						
							|  |  |  | #include <QJsonObject>
 | 
					
						
							|  |  |  | #include <QRegularExpression>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "util/iot/homeassistant.h"
 | 
					
						
							|  |  |  | #include "util/simpleserializer.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HomeAssistantDevice::HomeAssistantDevice(const QString& apiKey, const QString& url, const QString &deviceId, | 
					
						
							|  |  |  |                                         const QStringList &controls, const QStringList &sensors, | 
					
						
							|  |  |  |                                         DeviceDiscoverer::DeviceInfo *info) : | 
					
						
							|  |  |  |     Device(info), | 
					
						
							| 
									
										
										
										
											2022-09-16 11:41:40 +01:00
										 |  |  |     m_deviceId(deviceId), | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |     m_apiKey(apiKey), | 
					
						
							| 
									
										
										
										
											2022-09-16 11:41:40 +01:00
										 |  |  |     m_url(url) | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     m_entities = controls; | 
					
						
							|  |  |  |     m_entities.append(sensors); | 
					
						
							|  |  |  |     m_networkManager = new QNetworkAccessManager(); | 
					
						
							|  |  |  |     QObject::connect( | 
					
						
							|  |  |  |         m_networkManager, | 
					
						
							|  |  |  |         &QNetworkAccessManager::finished, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &HomeAssistantDevice::handleReply | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HomeAssistantDevice::~HomeAssistantDevice() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QObject::disconnect( | 
					
						
							|  |  |  |         m_networkManager, | 
					
						
							|  |  |  |         &QNetworkAccessManager::finished, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &HomeAssistantDevice::handleReply | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     delete m_networkManager; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HomeAssistantDevice::getState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Get state for all entities of the device
 | 
					
						
							|  |  |  |     for (auto entity : m_entities) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         QUrl url(m_url + "/api/states/" + entity); | 
					
						
							|  |  |  |         QNetworkRequest request(url); | 
					
						
							|  |  |  |         request.setRawHeader("Authorization", "Bearer " + m_apiKey.toLocal8Bit()); | 
					
						
							|  |  |  |         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  |         QNetworkReply *reply = m_networkManager->get(request); | 
					
						
							|  |  |  |         recordGetRequest(reply); | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HomeAssistantDevice::setState(const QString &controlId, bool state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QString domain = controlId.left(controlId.indexOf(".")); | 
					
						
							|  |  |  |     QUrl url(m_url + "/api/services/" + domain + "/turn_" + (state ? "on" : "off")); | 
					
						
							|  |  |  |     QNetworkRequest request(url); | 
					
						
							|  |  |  |     request.setRawHeader("Authorization", "Bearer " + m_apiKey.toLocal8Bit()); | 
					
						
							|  |  |  |     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QJsonObject object { | 
					
						
							|  |  |  |         {"entity_id", controlId} | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     QJsonDocument document; | 
					
						
							|  |  |  |     document.setObject(object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_networkManager->post(request, document.toJson()); | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // 750ms guard, to try to avoid toggling of widget, while state updates on server
 | 
					
						
							|  |  |  |     // Perhaps should be a setting
 | 
					
						
							|  |  |  |     recordSetRequest(controlId, 750); | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HomeAssistantDevice::handleReply(QNetworkReply* reply) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (reply) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!reply->error()) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  |             QByteArray data = reply->readAll(); | 
					
						
							|  |  |  |             QJsonParseError error; | 
					
						
							|  |  |  |             QJsonDocument document = QJsonDocument::fromJson(data, &error); | 
					
						
							|  |  |  |             if (!document.isNull()) | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  |                 //qDebug() << "Received " << document;
 | 
					
						
							|  |  |  |                 // POSTs to /api/services return an array, GETs from /api/states return an object
 | 
					
						
							|  |  |  |                 if (document.isObject()) | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |                 { | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  |                     QJsonObject obj = document.object(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if (obj.contains(QStringLiteral("entity_id")) && obj.contains(QStringLiteral("state"))) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         QString entityId = obj.value(QStringLiteral("entity_id")).toString(); | 
					
						
							|  |  |  |                         if (getAfterSet(reply, entityId)) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             QHash<QString, QVariant> status; | 
					
						
							|  |  |  |                             QString state = obj.value(QStringLiteral("state")).toString(); | 
					
						
							|  |  |  |                             bool dOk; | 
					
						
							|  |  |  |                             bool iOk; | 
					
						
							|  |  |  |                             int i = state.toInt(&iOk); | 
					
						
							|  |  |  |                             double d = state.toDouble(&dOk); | 
					
						
							|  |  |  |                             if ((state == "on") || (state == "playing")) { | 
					
						
							|  |  |  |                                 status.insert(entityId, 1); | 
					
						
							|  |  |  |                             } else if ((state == "off") || (state == "paused")) { | 
					
						
							|  |  |  |                                 status.insert(entityId, 0); | 
					
						
							|  |  |  |                             } else if (iOk) { | 
					
						
							|  |  |  |                                 status.insert(entityId, i); | 
					
						
							|  |  |  |                             } else if (dOk) { | 
					
						
							|  |  |  |                                 status.insert(entityId, d); | 
					
						
							|  |  |  |                             } else { | 
					
						
							|  |  |  |                                 status.insert(entityId, state); | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                             emit deviceUpdated(status); | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  |                 qDebug() << "HomeAssistantDevice::handleReply: Error parsing JSON: " << error.errorString() << " at offset " << error.offset; | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qDebug() << "HomeAssistantDevice::handleReply: error: " << reply->error(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  |         removeGetRequest(reply); | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |         reply->deleteLater(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         qDebug() << "HomeAssistantDevice::handleReply: reply is null"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HomeAssistantDeviceDiscoverer::HomeAssistantDeviceDiscoverer(const QString& apiKey, const QString& url) : | 
					
						
							|  |  |  |     m_apiKey(apiKey), | 
					
						
							|  |  |  |     m_url(url) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_networkManager = new QNetworkAccessManager(); | 
					
						
							|  |  |  |     QObject::connect( | 
					
						
							|  |  |  |         m_networkManager, | 
					
						
							|  |  |  |         &QNetworkAccessManager::finished, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &HomeAssistantDeviceDiscoverer::handleReply | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HomeAssistantDeviceDiscoverer::~HomeAssistantDeviceDiscoverer() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QObject::disconnect( | 
					
						
							|  |  |  |         m_networkManager, | 
					
						
							|  |  |  |         &QNetworkAccessManager::finished, | 
					
						
							|  |  |  |         this, | 
					
						
							|  |  |  |         &HomeAssistantDeviceDiscoverer::handleReply | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     delete m_networkManager; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HomeAssistantDeviceDiscoverer::getDevices() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QUrl url(m_url+ "/api/template"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QNetworkRequest request(url); | 
					
						
							|  |  |  |     request.setRawHeader("Authorization", "Bearer " + m_apiKey.toLocal8Bit()); | 
					
						
							|  |  |  |     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Use templates to get a list of devices and associated entities
 | 
					
						
							|  |  |  |     QString tpl = | 
					
						
							|  |  |  |     "{% set devices = states | map(attribute='entity_id') | map('device_id') | unique | reject('eq',None)| list %}\n" | 
					
						
							|  |  |  |     "{%- set ns = namespace(devices = []) %}\n" | 
					
						
							|  |  |  |     "{%- for device in devices %}\n" | 
					
						
							|  |  |  |     "  {%- set entities = device_entities(device) | list %}\n" | 
					
						
							|  |  |  |     "  {%- if entities %}\n" | 
					
						
							|  |  |  |     "    {%- set ens = namespace(entityobjs = []) %}\n" | 
					
						
							|  |  |  |     "    {%- for entity in entities %}\n" | 
					
						
							|  |  |  |     "      {%- set entityobj = {'entity_id': entity, 'name': state_attr(entity,'friendly_name'), 'unit_of_measurement': state_attr(entity,'unit_of_measurement')} %}\n" | 
					
						
							|  |  |  |     "      {%- set ens.entityobjs = ens.entityobjs + [ entityobj ] %}\n" | 
					
						
							|  |  |  |     "    {%- endfor %}\n" | 
					
						
							|  |  |  |     "    {%- set obj = {'device_id': device, 'name': device_attr(device,'name'), 'name_by_user': device_attr(device,'name_by_user'), 'model': device_attr(device,'model'), 'entities': ens.entityobjs } %}\n" | 
					
						
							|  |  |  |     "    {%- set ns.devices = ns.devices + [ obj ] %}\n" | 
					
						
							|  |  |  |     "  {%- endif %}\n" | 
					
						
							|  |  |  |     "{%- endfor %}\n" | 
					
						
							|  |  |  |     "{{ ns.devices | tojson }}"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QJsonObject object { | 
					
						
							|  |  |  |         {"template", tpl} | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     QJsonDocument document; | 
					
						
							|  |  |  |     document.setObject(object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_networkManager->post(request, document.toJson()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void HomeAssistantDeviceDiscoverer::handleReply(QNetworkReply* reply) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (reply) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!reply->error()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             QList<DeviceInfo> devices; | 
					
						
							|  |  |  |             QByteArray data = reply->readAll(); | 
					
						
							|  |  |  |             //qDebug() << "Received " << data;
 | 
					
						
							|  |  |  |             QJsonParseError error; | 
					
						
							|  |  |  |             QJsonDocument document = QJsonDocument::fromJson(data, &error); | 
					
						
							|  |  |  |             if (!document.isNull()) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (document.isArray()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     for (auto deviceRef : document.array()) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         QJsonObject deviceObj = deviceRef.toObject(); | 
					
						
							|  |  |  |                         if (deviceObj.contains(QStringLiteral("device_id")) && deviceObj.contains(QStringLiteral("entities"))) | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             QJsonArray entitiesArray = deviceObj.value(QStringLiteral("entities")).toArray(); | 
					
						
							|  |  |  |                             if (entitiesArray.size() > 0) | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 DeviceInfo info; | 
					
						
							|  |  |  |                                 info.m_id = deviceObj.value(QStringLiteral("device_id")).toString(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                 if (deviceObj.contains(QStringLiteral("name_by_user"))) { | 
					
						
							|  |  |  |                                     info.m_name = deviceObj.value(QStringLiteral("name_by_user")).toString(); | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                                 if (info.m_name.isEmpty() && deviceObj.contains(QStringLiteral("name"))) { | 
					
						
							|  |  |  |                                     info.m_name = deviceObj.value(QStringLiteral("name")).toString(); | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                                 if (deviceObj.contains(QStringLiteral("model"))) { | 
					
						
							|  |  |  |                                     info.m_model = deviceObj.value(QStringLiteral("model")).toString(); | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                 for (auto entityRef : entitiesArray) | 
					
						
							|  |  |  |                                 { | 
					
						
							|  |  |  |                                     QJsonObject entityObj = entityRef.toObject(); | 
					
						
							|  |  |  |                                     QString entity = entityObj.value(QStringLiteral("entity_id")).toString(); | 
					
						
							|  |  |  |                                     QString name = entityObj.value(QStringLiteral("name")).toString(); | 
					
						
							|  |  |  |                                     QString domain = entity.left(entity.indexOf('.')); | 
					
						
							|  |  |  |                                     if (domain == "binary_sensor") | 
					
						
							|  |  |  |                                     { | 
					
						
							|  |  |  |                                         SensorInfo *sensorInfo = new SensorInfo(); | 
					
						
							|  |  |  |                                         sensorInfo->m_name = name; | 
					
						
							|  |  |  |                                         sensorInfo->m_id = entity; | 
					
						
							|  |  |  |                                         sensorInfo->m_type = DeviceDiscoverer::BOOL; | 
					
						
							|  |  |  |                                         sensorInfo->m_units = entityObj.value(QStringLiteral("unit_of_measurement")).toString(); | 
					
						
							|  |  |  |                                         info.m_sensors.append(sensorInfo); | 
					
						
							|  |  |  |                                     } | 
					
						
							|  |  |  |                                     else if (domain == "sensor") | 
					
						
							|  |  |  |                                     { | 
					
						
							|  |  |  |                                         SensorInfo *sensorInfo = new SensorInfo(); | 
					
						
							|  |  |  |                                         sensorInfo->m_name = name; | 
					
						
							|  |  |  |                                         sensorInfo->m_id = entity; | 
					
						
							|  |  |  |                                         sensorInfo->m_type = DeviceDiscoverer::FLOAT; // FIXME: Auto?
 | 
					
						
							|  |  |  |                                         sensorInfo->m_units = entityObj.value(QStringLiteral("unit_of_measurement")).toString(); | 
					
						
							|  |  |  |                                         info.m_sensors.append(sensorInfo); | 
					
						
							|  |  |  |                                     } | 
					
						
							|  |  |  |                                     else if ((domain == "switch") || (domain == "light") || (domain == "media_player")) // Entities that support turn_on/turn_off
 | 
					
						
							|  |  |  |                                     { | 
					
						
							|  |  |  |                                         ControlInfo *controlInfo = new ControlInfo(); | 
					
						
							|  |  |  |                                         controlInfo->m_name = name; | 
					
						
							|  |  |  |                                         controlInfo->m_id = entity; | 
					
						
							|  |  |  |                                         controlInfo->m_type = DeviceDiscoverer::BOOL; | 
					
						
							|  |  |  |                                         info.m_controls.append(controlInfo); | 
					
						
							|  |  |  |                                     } | 
					
						
							|  |  |  |                                     else | 
					
						
							|  |  |  |                                     { | 
					
						
							|  |  |  |                                         qDebug() << "HomeAssistantDeviceDiscoverer::handleReply: Unsupported domain: " << domain; | 
					
						
							|  |  |  |                                     } | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                                 if ((info.m_controls.size() > 0) || (info.m_sensors.size() > 0)) | 
					
						
							|  |  |  |                                 { | 
					
						
							|  |  |  |                                     devices.append(info); | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                             else | 
					
						
							|  |  |  |                             { | 
					
						
							|  |  |  |                                 //qDebug() << "HomeAssistantDeviceDiscoverer::handleReply: No entities " << deviceObj.value(QStringLiteral("device_id")).toString();
 | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         else | 
					
						
							|  |  |  |                         { | 
					
						
							|  |  |  |                             //qDebug() << "HomeAssistantDeviceDiscoverer::handleReply: device_id or entities missing";
 | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     qDebug() << "HomeAssistantDeviceDiscoverer::handleReply: Document is not an array: " << document; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2022-09-16 14:29:55 +01:00
										 |  |  |                 qDebug() << "HomeAssistantDeviceDiscoverer::handleReply: Error parsing JSON: " << error.errorString() << " at offset " << error.offset; | 
					
						
							| 
									
										
										
										
											2022-09-16 10:01:25 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |             emit deviceList(devices); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             qDebug() << "HomeAssistantDeviceDiscoverer::handleReply: error: " << reply->error() << ":" << reply->errorString(); | 
					
						
							|  |  |  |             // Get QNetworkReply::AuthenticationRequiredError if token is invalid
 | 
					
						
							|  |  |  |             if (reply->error() == QNetworkReply::AuthenticationRequiredError) { | 
					
						
							|  |  |  |                 emit error("Home Assistant: Authentication failed. Check access token is valid."); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 emit error(QString("Home Assistant: Network error. %1").arg(reply->errorString())); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         reply->deleteLater(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         qDebug() << "HomeAssistantDeviceDiscoverer::handleReply: reply is null"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |