This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
Michael Zanetti 6a35dd61e4 workaround responses for hue getting lost
missing responses caused the plugin to hang. This workaround assumes
a missing response after 2 secs and proceeds with sending.
2019-04-01 20:48:17 +02:00

422 lines
12 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2013 Michael Zanetti <michael_zanetti@gmx.net> *
* *
* This file is part of guh. *
* *
* Guh 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, version 2 of the License. *
* *
* Guh 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 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "light.h"
#include "huebridgeconnection.h"
#include <QColor>
#include <QDebug>
#include <QGenericMatrix>
Light::Light(const QHostAddress &ip, const QString &username, int id, QObject *parent):
QObject(parent),
m_bridge(new HueBridgeConnection(this)),
m_ip(ip),
m_username(username),
m_id(id),
m_on(false),
m_busyStateChangeId(-1),
m_hueDirty(false),
m_satDirty(false),
m_briDirty(false),
m_ctDirty(false),
m_xyDirty(false)
{
m_busyTimeout.setSingleShot(true);
m_busyTimeout.setInterval(2000);
connect(&m_busyTimeout, &QTimer::timeout, [this]{ setStateFinished(m_busyStateChangeId, QVariant()); });
}
QHostAddress Light::ip() const
{
return m_ip;
}
QString Light::username() const
{
return m_username;
}
int Light::id() const
{
return m_id;
}
QString Light::name() const
{
return m_name;
}
void Light::setName(const QString &name)
{
if (m_name != name) {
QVariantMap params;
params.insert("name", name);
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id), params, this, "setDescriptionFinished");
}
}
QString Light::modelId() const
{
return m_modelId;
}
void Light::setModelId(const QString &modelId)
{
if (m_modelId != modelId) {
m_modelId = modelId;
}
}
QString Light::type() const
{
return m_type;
}
void Light::setType(const QString &type)
{
if (m_type != type) {
m_type = type;
}
}
QString Light::swversion() const
{
return m_swversion;
}
void Light::setSwversion(const QString &swversion)
{
if (m_swversion != swversion) {
m_swversion = swversion;
}
}
bool Light::on() const
{
return m_on;
}
void Light::setOn(bool on)
{
if (m_on != on) {
QVariantMap params;
params.insert("on", on);
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
}
}
quint8 Light::bri() const
{
return m_bri;
}
void Light::setBri(quint8 bri)
{
if (m_bri != bri) {
qDebug() << "setting brightness to" << bri << m_busyStateChangeId;
if (m_busyStateChangeId == -1) {
QVariantMap params;
if (bri == 0) {
params.insert("bri", bri);
params.insert("on", false);
} else {
params.insert("bri", bri);
params.insert("on", true);
}
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else {
m_dirtyBri = bri;
m_briDirty = true;
}
}
}
quint16 Light::hue() const
{
return m_hue;
}
void Light::setHue(quint16 hue)
{
if (m_hue != hue) {
m_hue = hue;
emit stateChanged();
}
}
quint8 Light::sat() const
{
return m_sat;
}
void Light::setSat(quint8 sat)
{
if (m_sat != sat) {
m_sat = sat;
emit stateChanged();
}
}
QColor Light::color() const
{
return QColor::fromHsv(m_hue * 360 / 65535, m_sat, 255);
}
void Light::setColor(const QColor &color)
{
// Transform from RGB to Hue/Sat
quint16 hue = color.hue() * 65535 / 360;
quint8 sat = color.saturation();
qDebug() << "setting color" << color;
if (m_busyStateChangeId == -1) {
QVariantMap params;
params.insert("hue", hue);
params.insert("sat", sat);
// FIXME: There is a bug in the API that it doesn't report back the set state of "sat"
// Lets just assume it always succeeds
m_sat = sat;
params.insert("on", true);
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else {
m_dirtyHue = hue;
m_hueDirty = true;
m_dirtySat = sat;
m_satDirty = true;
}
}
QPointF Light::xy() const
{
return m_xy;
}
void Light::setXy(const QPointF &xy)
{
if (m_xy != xy) {
m_xy = xy;
emit stateChanged();
}
}
quint16 Light::ct() const
{
return m_ct;
}
void Light::setCt(quint16 ct)
{
if (m_busyStateChangeId == -1) {
QVariantMap params;
params.insert("ct", ct);
params.insert("on", true);
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else {
m_dirtyCt = ct;
m_ctDirty = true;
}
}
QString Light::alert() const
{
return m_alert;
}
void Light::setAlert(const QString &alert)
{
if (m_alert != alert) {
m_alert = alert;
emit stateChanged();
}
}
QString Light::effect() const
{
return m_effect;
}
void Light::setEffect(const QString &effect)
{
if (m_effect != effect) {
QVariantMap params;
params.insert("effect", effect);
if (effect != "none") {
params.insert("on", true);
}
m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
}
}
Light::ColorMode Light::colorMode() const
{
return m_colormode;
}
bool Light::reachable() const
{
return m_reachable;
}
void Light::refresh()
{
m_bridge->get(m_ip, m_username, "lights/" + QString::number(m_id), this, "responseReceived");
}
void Light::setReachable(bool reachable)
{
if (m_reachable != reachable) {
m_reachable = reachable;
emit stateChanged();
}
}
void Light::responseReceived(int id, const QVariant &response)
{
Q_UNUSED(id)
QVariantMap attributes = response.toMap();
m_name = attributes.value("name").toString();
setModelId(attributes.value("modelid").toString());
setType(attributes.value("type").toString());
setSwversion(attributes.value("swversion").toString());
QVariantMap stateMap = attributes.value("state").toMap();
m_on = stateMap.value("on").toBool();
m_bri = stateMap.value("bri").toInt();
m_hue = stateMap.value("hue").toInt();
m_sat = stateMap.value("sat").toInt();
m_xy = stateMap.value("xy").toPointF();
m_ct = stateMap.value("ct").toInt();
m_alert = stateMap.value("alert").toString();
m_effect = stateMap.value("effect").toString();
QString colorModeString = stateMap.value("colormode").toString();
if (colorModeString == "hs") {
m_colormode = ColorModeHS;
} else if (colorModeString == "xy") {
m_colormode = ColorModeXY;
} else if (colorModeString == "ct") {
m_colormode = ColorModeCT;
}
m_reachable = stateMap.value("reachable").toBool();
emit stateChanged();
//qDebug() << "got light response" << m_modelId << m_type << m_swversion << m_on << m_bri << m_reachable;
}
void Light::setDescriptionFinished(int id, const QVariant &response)
{
Q_UNUSED(id)
QVariantMap result = response.toList().first().toMap();
if (result.contains("success")) {
QVariantMap successMap = result.value("success").toMap();
if (successMap.contains("/lights/" + QString::number(m_id) + "/name")) {
m_name = successMap.value("/lights/" + QString::number(m_id) + "/name").toString();
emit stateChanged();
}
}
}
void Light::setStateFinished(int id, const QVariant &response)
{
foreach (const QVariant &resultVariant, response.toList()) {
QVariantMap result = resultVariant.toMap();
if (result.contains("success")) {
QVariantMap successMap = result.value("success").toMap();
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/on")) {
m_on = successMap.value("/lights/" + QString::number(m_id) + "/state/on").toBool();
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/hue")) {
m_hue = successMap.value("/lights/" + QString::number(m_id) + "/state/hue").toInt();
m_colormode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/bri")) {
m_bri = successMap.value("/lights/" + QString::number(m_id) + "/state/bri").toInt();
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/sat")) {
m_sat = successMap.value("/lights/" + QString::number(m_id) + "/state/sat").toInt();
m_colormode = ColorModeHS;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/xy")) {
m_xy = successMap.value("/lights/" + QString::number(m_id) + "/state/xy").toPoint();
m_colormode = ColorModeXY;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/ct")) {
m_ct = successMap.value("/lights/" + QString::number(m_id) + "/state/ct").toInt();
m_colormode = ColorModeCT;
}
if (successMap.contains("/lights/" + QString::number(m_id) + "/state/effect")) {
m_effect = successMap.value("/lights/" + QString::number(m_id) + "/state/effect").toString();
}
}
}
emit stateChanged();
if (m_busyStateChangeId == id) {
m_busyTimeout.stop();
m_busyStateChangeId = -1;
if (m_hueDirty || m_satDirty || m_briDirty) {
QVariantMap params;
if (m_hueDirty) {
params.insert("hue", m_dirtyHue);
m_hueDirty = false;
}
if (m_satDirty) {
params.insert("sat", m_dirtySat);
m_satDirty = false;
}
if (m_briDirty) {
params.insert("bri", m_dirtyBri);
m_briDirty = false;
}
// FIXME: There is a bug in the API that it doesn't report back the set state of "sat"
// Lets just assume it always succeeds
m_sat = m_dirtySat;
m_busyStateChangeId = m_bridge->put(QHostAddress(m_ip), m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else if(m_ctDirty) {
QVariantMap params;
params.insert("ct", m_dirtyCt);
m_ctDirty = false;
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
} else if (m_xyDirty) {
QVariantMap params;
QVariantList xyList;
xyList << m_dirtyXy.x() << m_dirtyXy.y();
params.insert("xy", xyList);
m_xyDirty = false;
m_busyStateChangeId = m_bridge->put(m_ip, m_username, "lights/" + QString::number(m_id) + "/state", params, this, "setStateFinished");
m_busyTimeout.start();
}
}
}