Thermostats: Adjust to new thermostat interface

master
Michael Zanetti 2020-12-29 13:21:15 +01:00
parent cdff51655f
commit 230d0c49df
9 changed files with 201 additions and 77 deletions

View File

@ -158,17 +158,6 @@ bool EqivaBluetooth::available() const
return m_available;
}
bool EqivaBluetooth::enabled() const
{
return m_enabled;
}
int EqivaBluetooth::setEnabled(bool enabled)
{
emit enabledChanged();
return setTargetTemperature(enabled ? m_cachedTargetTemp : 4.5);
}
bool EqivaBluetooth::locked() const
{
return m_locked;
@ -199,7 +188,7 @@ int EqivaBluetooth::setBoostEnabled(bool enabled)
qreal EqivaBluetooth::targetTemperature() const
{
return m_enabled ? m_targetTemp : m_cachedTargetTemp;
return m_targetTemp;
}
int EqivaBluetooth::setTargetTemperature(qreal targetTemperature)
@ -211,7 +200,6 @@ int EqivaBluetooth::setTargetTemperature(qreal targetTemperature)
stream << static_cast<quint8>(4.5 * 2); // 4.5 degrees is off
} else {
stream << static_cast<quint8>(targetTemperature * 2); // Temperature *2 (thing only supports .5 precision)
m_cachedTargetTemp = targetTemperature;
}
return enqueue("SetTargetTemperature", data);
}
@ -418,11 +406,10 @@ void EqivaBluetooth::characteristicChanged(const QLowEnergyCharacteristic &info,
quint8 mode = (lockAndMode & 0x0F);
m_targetTemp = 1.0 * rawTemp / 2;
m_enabled = m_targetTemp >= 5;
if (m_targetTemp < 5) {
m_targetTemp = 5;
}
qCDebug(dcEQ3()) << m_name << "Status notification received: Enabled:" << m_enabled << "Temp:" << m_targetTemp << "Keylock:" << m_locked << "Window open:" << m_windowOpen << "Mode:" << mode << "Valve open:" << m_valveOpen << "Boost:" << m_boostEnabled << "Battery critical" << m_batteryCritical;
qCDebug(dcEQ3()) << m_name << "Status notification received: Enabled:" << "Temp:" << m_targetTemp << "Keylock:" << m_locked << "Window open:" << m_windowOpen << "Mode:" << mode << "Valve open:" << m_valveOpen << "Boost:" << m_boostEnabled << "Battery critical" << m_batteryCritical;
m_boostEnabled = false;
switch (mode) {
@ -449,7 +436,6 @@ void EqivaBluetooth::characteristicChanged(const QLowEnergyCharacteristic &info,
break;
}
emit enabledChanged();
emit lockedChanged();
emit boostEnabledChanged();
emit modeChanged();

View File

@ -74,7 +74,6 @@ public:
signals:
void availableChanged();
void enabledChanged();
void lockedChanged();
void boostEnabledChanged();
void modeChanged();
@ -107,11 +106,9 @@ private:
QString m_name;
bool m_available = false;
bool m_enabled = false;
bool m_locked = false;
bool m_boostEnabled = false;
qreal m_targetTemp = 0;
qreal m_cachedTargetTemp = 0;
Mode m_mode = ModeAuto;
bool m_windowOpen = false;
quint8 m_valveOpen = 0;

View File

@ -175,9 +175,9 @@ void IntegrationPluginEQ3::setupThing(ThingSetupInfo *info)
thing->setStateValue(eqivaBluetoothConnectedStateTypeId, eqivaDevice->available());
});
// Power state
thing->setStateValue(eqivaBluetoothPowerStateTypeId, eqivaDevice->enabled());
connect(eqivaDevice, &EqivaBluetooth::enabledChanged, thing, [thing, eqivaDevice](){
thing->setStateValue(eqivaBluetoothPowerStateTypeId, eqivaDevice->enabled());
thing->setStateValue(eqivaBluetoothHeatingOnStateTypeId, eqivaDevice->valveOpen() > 0);
connect(eqivaDevice, &EqivaBluetooth::valveOpenChanged, thing, [thing, eqivaDevice](){
thing->setStateValue(eqivaBluetoothHeatingOnStateTypeId, eqivaDevice->valveOpen() > 0);
});
// Boost state
thing->setStateValue(eqivaBluetoothBoostStateTypeId, eqivaDevice->boostEnabled());
@ -343,9 +343,7 @@ void IntegrationPluginEQ3::executeAction(ThingActionInfo *info)
int commandId;
EqivaBluetooth *eqivaDevice = m_eqivaDevices.value(thing);
if (action.actionTypeId() == eqivaBluetoothPowerActionTypeId) {
commandId = eqivaDevice->setEnabled(action.param(eqivaBluetoothPowerActionPowerParamTypeId).value().toBool());
} else if (action.actionTypeId() == eqivaBluetoothTargetTemperatureActionTypeId) {
if (action.actionTypeId() == eqivaBluetoothTargetTemperatureActionTypeId) {
commandId = eqivaDevice->setTargetTemperature(action.param(eqivaBluetoothTargetTemperatureActionTargetTemperatureParamTypeId).value().toReal());
} else if (action.actionTypeId() == eqivaBluetoothLockActionTypeId) {
commandId = eqivaDevice->setLocked(action.param(eqivaBluetoothLockActionLockParamTypeId).value().toBool());

View File

@ -533,7 +533,7 @@
"id": "3c51327b-a823-4479-bd4b-f4ba64267ed8",
"name": "eqivaBluetooth",
"displayName": "Eqiva Bluetooth Smart Radiator Thermostat",
"interfaces": ["heating", "thermostat", "wirelessconnectable", "battery"],
"interfaces": ["thermostat", "wirelessconnectable", "battery"],
"createMethods": ["discovery"],
"setupMethod": "JustAdd",
"paramTypes": [
@ -555,13 +555,11 @@
},
{
"id": "cc5700f3-28b0-4653-b46d-770a99e6cea7",
"name": "power",
"displayName": "On",
"displayNameEvent": "On changed",
"displayNameAction": "Turn on/off",
"name": "heatingOn",
"displayName": "Heating",
"displayNameEvent": "Heating changed",
"type": "bool",
"defaultValue": false,
"writable": true
"defaultValue": false
},
{
"id": "5e9035ed-317d-42ee-b7f4-2996c75ba939",

View File

@ -642,12 +642,19 @@ void IntegrationPluginGenericThings::executeAction(ThingActionInfo *info)
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
} else if (thing->thingClassId() == thermostatThingClassId) {
if (action.actionTypeId() == thermostatTemperatureSensorInputActionTypeId) {
thing->setStateValue(thermostatTemperatureSensorInputStateTypeId, action.param(thermostatTemperatureSensorInputActionTemperatureSensorInputParamTypeId).value());
if (action.actionTypeId() == thermostatTemperatureActionTypeId) {
thing->setStateValue(thermostatTemperatureStateTypeId, action.param(thermostatTemperatureActionTemperatureParamTypeId).value());
} else if (action.actionTypeId() == thermostatTargetTemperatureActionTypeId) {
thing->setStateValue(thermostatTargetTemperatureStateTypeId, action.param(thermostatTargetTemperatureActionTargetTemperatureParamTypeId).value());
} else if (action.actionTypeId() == thermostatPowerActionTypeId) {
thing->setStateValue(thermostatPowerStateTypeId, action.param(thermostatPowerActionPowerParamTypeId).value());
double minSetting = thing->setting(thermostatSettingsMinTargetTemperatureParamTypeId).toDouble();
double maxSetting = thing->setting(thermostatSettingsMaxTargetTemperatureParamTypeId).toDouble();
double newTemp = action.param(thermostatTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble();
newTemp = qMax(newTemp, minSetting);
newTemp = qMin(newTemp, maxSetting);
thing->setStateValue(thermostatTargetTemperatureStateTypeId, newTemp);
} else if (action.actionTypeId() == thermostatHeatingOnActionTypeId) {
thing->setStateValue(thermostatHeatingOnStateTypeId, action.param(thermostatHeatingOnActionHeatingOnParamTypeId).value());
} else if (action.actionTypeId() == thermostatCoolingOnActionTypeId) {
thing->setStateValue(thermostatCoolingOnStateTypeId, action.param(thermostatCoolingOnActionCoolingOnParamTypeId).value());
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
@ -655,6 +662,24 @@ void IntegrationPluginGenericThings::executeAction(ThingActionInfo *info)
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == heatingThingClassId) {
if (action.actionTypeId() == heatingPowerActionTypeId) {
thing->setStateValue(heatingPowerStateTypeId, action.paramValue(heatingPowerActionPowerParamTypeId).toBool());
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == coolingThingClassId) {
if (action.actionTypeId() == coolingPowerActionTypeId) {
thing->setStateValue(coolingPowerStateTypeId, action.paramValue(coolingPowerActionPowerParamTypeId).toBool());
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == sgReadyThingClassId) {
if (action.actionTypeId() == sgReadyRelay1ActionTypeId) {
thing->setStateValue(sgReadyRelay1StateTypeId, action.param(sgReadyRelay1ActionRelay1ParamTypeId).value());
@ -851,15 +876,17 @@ void IntegrationPluginGenericThings::moveBlindToAngle(Action action, Thing *thin
void IntegrationPluginGenericThings::thermostatCheckPowerOutputState(Thing *thing)
{
double targetTemperature = thing->stateValue(thermostatTargetTemperatureStateTypeId).toDouble();
double actualTemperature = thing->stateValue(thermostatTemperatureSensorInputStateTypeId).toDouble();
double actualTemperature = thing->stateValue(thermostatTemperatureStateTypeId).toDouble();
double temperatureDifference = thing->setting(thermostatSettingsTemperatureDifferenceParamTypeId).toDouble();
if (actualTemperature <= (targetTemperature-temperatureDifference)) {
thing->setStateValue(thermostatPowerStateTypeId, true);
thing->setStateValue(thermostatHeatingOnStateTypeId, true);
} else if (actualTemperature >= targetTemperature) {
thing->setStateValue(thermostatPowerStateTypeId, false);
} else {
//Keep actual state
//Possible improvement add boost action where powerState = true is forced inside the hysteresis
thing->setStateValue(thermostatHeatingOnStateTypeId, false);
}
if (actualTemperature >= (targetTemperature+temperatureDifference)) {
thing->setStateValue(thermostatCoolingOnStateTypeId, true);
} else if (actualTemperature <= targetTemperature) {
thing->setStateValue(thermostatCoolingOnStateTypeId, false);
}
}

View File

@ -1035,7 +1035,7 @@
"name": "thermostat",
"displayName": "Generic thermostat",
"createMethods": ["user"],
"interfaces": ["thermostat", "heating"],
"interfaces": ["thermostat"],
"settingsTypes": [
{
"id": "64bf308f-a543-4e02-b787-1a1714c1f978",
@ -1045,12 +1045,32 @@
"unit": "DegreeCelsius",
"minValue": 0.00,
"defaultValue": 2.00
},
{
"id": "67451c97-50e1-4ea6-ac43-4386fbd26698",
"name": "minTargetTemperature",
"displayName": "Minimum temperature",
"type": "double",
"unit": "DegreeCelsius",
"minValue": -20,
"maxValue": 49,
"defaultValue": -20
},
{
"id": "85608dd5-7e67-4c98-9e62-b97411681048",
"name": "maxTargetTemperature",
"displayName": "Maximum temperature",
"type": "double",
"unit": "DegreeCelsius",
"minValue": -19,
"maxValue": 50,
"defaultValue": 50
}
],
"stateTypes": [
{
"id": "0f808803-0e63-47df-b024-9685998ba663",
"name": "temperatureSensorInput",
"name": "temperature",
"displayName": "Temperature sensor input",
"displayNameEvent": "Temperature sensor input changed",
"displayNameAction": "Set temperature sensor input",
@ -1078,10 +1098,61 @@
},
{
"id": "1f6a0c39-4417-4e31-86db-9926cf81c345",
"name": "heatingOn",
"displayName": "Heating On/off",
"displayNameEvent": "Heating turned on/off",
"displayNameAction": "Turn heating on/off",
"type": "bool",
"defaultValue": false,
"writable": true,
"ioType": "digitalInput"
},
{
"id": "cab7d4bd-f612-4d12-b3a4-0649e189810f",
"name": "coolingOn",
"displayName": "Cooling On/off",
"displayNameEvent": "Cooling turned on/off",
"displayNameAction": "Turn cooling on/off",
"type": "bool",
"defaultValue": false,
"writable": true,
"ioType": "digitalInput"
}
]
},
{
"id": "e808fc5b-12fb-46d4-bb5d-25a4586bc0ba",
"name": "heating",
"displayName": "Generic heater",
"createMethods": ["user"],
"interfaces": ["heating"],
"stateTypes": [
{
"id": "bb6b5e3a-d4d9-4440-a098-0720c14ad679",
"name": "power",
"displayName": "On/off",
"displayNameEvent": "Turned on/off",
"displayNameAction": "Turn on/off",
"displayName": "Heater on/off",
"displayNameEvent": "Heater turned on/off",
"displayNameAction": "Turn heater on/off",
"type": "bool",
"defaultValue": false,
"writable": true,
"ioType": "digitalInput"
}
]
},
{
"id": "09edbc07-d382-48a4-9b16-99992014aff9",
"name": "cooling",
"displayName": "Generic cooler",
"createMethods": ["user"],
"interfaces": ["cooling"],
"stateTypes": [
{
"id": "918cfd2c-6692-4faa-acc6-18ebf93611ec",
"name": "power",
"displayName": "Cooler on/off",
"displayNameEvent": "Cooler turned on/off",
"displayNameAction": "Turn cooler on/off",
"type": "bool",
"defaultValue": false,
"writable": true,

View File

@ -70,7 +70,6 @@ void IntegrationPluginSimulation::setupThing(ThingSetupInfo *info)
thing->thingClassId() == extendedBlindThingClassId ||
thing->thingClassId() == rollerShutterThingClassId ||
thing->thingClassId() == fingerPrintSensorThingClassId ||
thing->thingClassId() == thermostatThingClassId ||
thing->thingClassId() == barcodeScannerThingClassId ||
thing->thingClassId() == contactSensorThingClassId) {
m_simulationTimers.insert(thing, new QTimer(thing));
@ -82,6 +81,48 @@ void IntegrationPluginSimulation::setupThing(ThingSetupInfo *info)
if (thing->thingClassId() == barcodeScannerThingClassId) {
m_simulationTimers.value(thing)->start(10000);
}
if (thing->thingClassId() == thermostatThingClassId) {
QTimer *t = new QTimer(thing);
connect(t, &QTimer::timeout, thing, [thing](){
double targetTemp = thing->stateValue(thermostatTargetTemperatureStateTypeId).toDouble();
double currentTemp = thing->stateValue(thermostatTemperatureStateTypeId).toDouble();
bool heatingOn = thing->stateValue(thermostatHeatingOnStateTypeId).toBool();
bool coolingOn = thing->stateValue(thermostatCoolingOnStateTypeId).toBool();
bool boost = thing->stateValue(thermostatBoostStateTypeId).toBool();
// When we're heating, temp increases slowly until it's up on par with target temp
if (heatingOn) {
double diff = targetTemp - currentTemp;
currentTemp += 0.005 + diff * (boost ? 0.2 : 0.1);
if (currentTemp >= targetTemp) {
thing->setStateValue(thermostatHeatingOnStateTypeId, false);
}
} else {
// Decrease 1% per interval to simulate drop of temperature (assuming it's cold outside)
currentTemp = currentTemp * 0.995;
// Start heating when we're more than 2 degrees lower than what we should be
if (currentTemp < targetTemp - 2) {
thing->setStateValue(thermostatHeatingOnStateTypeId, true);
}
}
if (coolingOn) {
double diff = targetTemp - currentTemp;
currentTemp += diff * 0.1;
if (currentTemp <= targetTemp) {
thing->setStateValue(thermostatCoolingOnStateTypeId, false);
}
} else {
if (currentTemp > targetTemp + 2) {
thing->setStateValue(thermostatCoolingOnStateTypeId, true);
}
}
thing->setStateValue(thermostatTemperatureStateTypeId, currentTemp);
});
t->start(10000);
}
if (thing->thingClassId() == contactSensorThingClassId) {
m_simulationTimers.value(thing)->start(10000);
@ -166,29 +207,20 @@ void IntegrationPluginSimulation::executeAction(ThingActionInfo *info)
}
if (thing->thingClassId() == thermostatThingClassId) {
if (action.actionTypeId() == thermostatPowerActionTypeId) {
bool power = action.param(thermostatPowerActionPowerParamTypeId).value().toBool();
if (!power && thing->stateValue(thermostatBoostStateTypeId).toBool()) {
thing->setStateValue(thermostatBoostStateTypeId, false);
}
qCDebug(dcSimulation()) << "Set power" << power << "for thermostat device" << thing->name();
thing->setStateValue(thermostatPowerStateTypeId, power);
return info->finish(Thing::ThingErrorNoError);
}
if (action.actionTypeId() == thermostatBoostActionTypeId) {
bool boost = action.param(thermostatBoostActionBoostParamTypeId).value().toBool();
if (boost && !thing->stateValue(thermostatPowerStateTypeId).toBool()) {
thing->setStateValue(thermostatPowerStateTypeId, true);
}
qCDebug(dcSimulation()) << "Set boost" << boost << "for thermostat device" << thing->name();
thing->setStateValue(thermostatBoostStateTypeId, boost);
m_simulationTimers.value(thing)->start(5 * 60 * 1000);
QTimer *t = new QTimer(thing);
t->setInterval(5 * 60 * 1000);
t->setSingleShot(true);
connect(t, &QTimer::timeout, t, &QTimer::deleteLater);
connect(t, &QTimer::timeout, thing, [thing](){
thing->setStateValue(thermostatBoostStateTypeId, false);
});
return info->finish(Thing::ThingErrorNoError);
}
if (action.actionTypeId() == thermostatTargetTemperatureActionTypeId) {
if (!thing->stateValue(thermostatPowerStateTypeId).toBool()) {
thing->setStateValue(thermostatPowerStateTypeId, true);
}
double targetTemp = action.param(thermostatTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble();
qCDebug(dcSimulation()) << "Set targetTemp" << targetTemp << "for thermostat device" << thing->name();
thing->setStateValue(thermostatTargetTemperatureStateTypeId, targetTemp);

View File

@ -161,7 +161,7 @@
"name": "heating",
"displayName": "Heating",
"createMethods": ["user"],
"interfaces": ["extendedheating"],
"interfaces": ["heating"],
"paramTypes": [ ],
"stateTypes": [
{
@ -228,7 +228,7 @@
"name": "thermostat",
"displayName": "Thermostat",
"createMethods": ["user"],
"interfaces": ["heating", "thermostat"],
"interfaces": ["thermostat"],
"paramTypes": [ ],
"stateTypes": [
{
@ -239,20 +239,35 @@
"displayNameAction": "Set target temperature",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 5,
"defaultValue": 21,
"minValue": 5,
"maxValue": 30,
"maxValue": 35,
"writable": true
},
{
"id": "f3df52f0-ee1d-4163-a7b5-95d8f22b8841",
"name": "temperature",
"displayName": "Current temperature",
"displayNameEvent": "Current temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 18
},
{
"id": "94a7d50c-df3b-4590-a89e-9dae40ad84fa",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"name": "heatingOn",
"displayName": "Heating on",
"displayNameEvent": "Heating turned on/off",
"type": "bool",
"defaultValue": false,
"writable": true
"defaultValue": false
},
{
"id": "4c696205-392a-45ed-aab5-3b7f97ddf286",
"name": "coolingOn",
"displayName": "Cooling on",
"displayNameEvent": "Cooling turned on/off",
"type": "bool",
"defaultValue": false
},
{
"id": "f892f660-87ff-458a-bfa0-5af08591233e",
@ -812,7 +827,7 @@
"name": "heatingRod",
"displayName": "Heating Rod",
"createMethods": ["user"],
"interfaces": ["connectable", "extendedheating", "temperaturesensor"],
"interfaces": ["connectable", "heating", "temperaturesensor"],
"paramTypes": [ ],
"stateTypes": [
{

View File

@ -46,7 +46,7 @@
"id": "1a7bb944-fb9c-490a-8a4c-794b27282292",
"name": "zone",
"displayName": "Zone",
"interfaces": ["heating", "temperaturesensor", "humiditysensor", "connectable"],
"interfaces": ["thermostat", "heating", "temperaturesensor", "humiditysensor", "connectable"],
"createMethods": ["auto"],
"paramTypes": [
{