mirror of https://github.com/arendst/Tasmota.git
Merge branch 'development' into new-windmeter-sensor
This commit is contained in:
commit
cb1b7739b6
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
OpenTherm.cpp - OpenTherm Communication Library For Arduino, ESP8266
|
||||
Copyright 2018, Ihor Melnyk
|
||||
*/
|
||||
|
||||
#include "OpenTherm.h"
|
||||
|
||||
OpenTherm::OpenTherm(int inPin, int outPin, bool isSlave):
|
||||
status(OpenThermStatus::NOT_INITIALIZED),
|
||||
inPin(inPin),
|
||||
outPin(outPin),
|
||||
isSlave(isSlave),
|
||||
response(0),
|
||||
responseStatus(OpenThermResponseStatus::NONE),
|
||||
responseTimestamp(0),
|
||||
handleInterruptCallback(NULL),
|
||||
processResponseCallback(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void OpenTherm::begin(void(*handleInterruptCallback)(void), void(*processResponseCallback)(unsigned long, int))
|
||||
{
|
||||
pinMode(inPin, INPUT);
|
||||
pinMode(outPin, OUTPUT);
|
||||
if (handleInterruptCallback != NULL) {
|
||||
this->handleInterruptCallback = handleInterruptCallback;
|
||||
attachInterrupt(digitalPinToInterrupt(inPin), handleInterruptCallback, CHANGE);
|
||||
}
|
||||
activateBoiler();
|
||||
status = OpenThermStatus::READY;
|
||||
this->processResponseCallback = processResponseCallback;
|
||||
}
|
||||
|
||||
void OpenTherm::begin(void(*handleInterruptCallback)(void))
|
||||
{
|
||||
begin(handleInterruptCallback, NULL);
|
||||
}
|
||||
|
||||
bool ICACHE_RAM_ATTR OpenTherm::isReady()
|
||||
{
|
||||
return status == OpenThermStatus::READY;
|
||||
}
|
||||
|
||||
int ICACHE_RAM_ATTR OpenTherm::readState() {
|
||||
return digitalRead(inPin);
|
||||
}
|
||||
|
||||
void OpenTherm::setActiveState() {
|
||||
digitalWrite(outPin, LOW);
|
||||
}
|
||||
|
||||
void OpenTherm::setIdleState() {
|
||||
digitalWrite(outPin, HIGH);
|
||||
}
|
||||
|
||||
void OpenTherm::activateBoiler() {
|
||||
setIdleState();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void OpenTherm::sendBit(bool high) {
|
||||
if (high) setActiveState(); else setIdleState();
|
||||
delayMicroseconds(500);
|
||||
if (high) setIdleState(); else setActiveState();
|
||||
delayMicroseconds(500);
|
||||
}
|
||||
|
||||
bool OpenTherm::sendRequestAync(unsigned long request)
|
||||
{
|
||||
//Serial.println("Request: " + String(request, HEX));
|
||||
noInterrupts();
|
||||
const bool ready = isReady();
|
||||
interrupts();
|
||||
|
||||
if (!ready)
|
||||
return false;
|
||||
|
||||
status = OpenThermStatus::REQUEST_SENDING;
|
||||
response = 0;
|
||||
responseStatus = OpenThermResponseStatus::NONE;
|
||||
|
||||
sendBit(HIGH); //start bit
|
||||
for (int i = 31; i >= 0; i--) {
|
||||
sendBit(bitRead(request, i));
|
||||
}
|
||||
sendBit(HIGH); //stop bit
|
||||
setIdleState();
|
||||
|
||||
status = OpenThermStatus::RESPONSE_WAITING;
|
||||
responseTimestamp = micros();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::sendRequest(unsigned long request)
|
||||
{
|
||||
if (!sendRequestAync(request)) return 0;
|
||||
while (!isReady()) {
|
||||
process();
|
||||
yield();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
bool OpenTherm::sendResponse(unsigned long request)
|
||||
{
|
||||
status = OpenThermStatus::REQUEST_SENDING;
|
||||
response = 0;
|
||||
responseStatus = OpenThermResponseStatus::NONE;
|
||||
|
||||
sendBit(HIGH); //start bit
|
||||
for (int i = 31; i >= 0; i--) {
|
||||
sendBit(bitRead(request, i));
|
||||
}
|
||||
sendBit(HIGH); //stop bit
|
||||
setIdleState();
|
||||
status = OpenThermStatus::READY;
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenThermResponseStatus OpenTherm::getLastResponseStatus()
|
||||
{
|
||||
return responseStatus;
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR OpenTherm::handleInterrupt()
|
||||
{
|
||||
if (isReady())
|
||||
{
|
||||
if (isSlave && readState() == HIGH) {
|
||||
status = OpenThermStatus::RESPONSE_WAITING;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long newTs = micros();
|
||||
if (status == OpenThermStatus::RESPONSE_WAITING) {
|
||||
if (readState() == HIGH) {
|
||||
status = OpenThermStatus::RESPONSE_START_BIT;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
else {
|
||||
status = OpenThermStatus::RESPONSE_INVALID;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
}
|
||||
else if (status == OpenThermStatus::RESPONSE_START_BIT) {
|
||||
if ((newTs - responseTimestamp < 750) && readState() == LOW) {
|
||||
status = OpenThermStatus::RESPONSE_RECEIVING;
|
||||
responseTimestamp = newTs;
|
||||
responseBitIndex = 0;
|
||||
}
|
||||
else {
|
||||
status = OpenThermStatus::RESPONSE_INVALID;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
}
|
||||
else if (status == OpenThermStatus::RESPONSE_RECEIVING) {
|
||||
if ((newTs - responseTimestamp) > 750) {
|
||||
if (responseBitIndex < 32) {
|
||||
response = (response << 1) | !readState();
|
||||
responseTimestamp = newTs;
|
||||
responseBitIndex++;
|
||||
}
|
||||
else { //stop bit
|
||||
status = OpenThermStatus::RESPONSE_READY;
|
||||
responseTimestamp = newTs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenTherm::process()
|
||||
{
|
||||
noInterrupts();
|
||||
OpenThermStatus st = status;
|
||||
unsigned long ts = responseTimestamp;
|
||||
interrupts();
|
||||
|
||||
if (st == OpenThermStatus::READY) return;
|
||||
unsigned long newTs = micros();
|
||||
if (st != OpenThermStatus::NOT_INITIALIZED && (newTs - ts) > 1000000) {
|
||||
status = OpenThermStatus::READY;
|
||||
responseStatus = OpenThermResponseStatus::TIMEOUT;
|
||||
if (processResponseCallback != NULL) {
|
||||
processResponseCallback(response, responseStatus);
|
||||
}
|
||||
}
|
||||
else if (st == OpenThermStatus::RESPONSE_INVALID) {
|
||||
status = OpenThermStatus::DELAY;
|
||||
responseStatus = OpenThermResponseStatus::INVALID;
|
||||
if (processResponseCallback != NULL) {
|
||||
processResponseCallback(response, responseStatus);
|
||||
}
|
||||
}
|
||||
else if (st == OpenThermStatus::RESPONSE_READY) {
|
||||
status = OpenThermStatus::DELAY;
|
||||
responseStatus = (isSlave ? isValidRequest(response) : isValidResponse(response)) ? OpenThermResponseStatus::SUCCESS : OpenThermResponseStatus::INVALID;
|
||||
if (processResponseCallback != NULL) {
|
||||
processResponseCallback(response, responseStatus);
|
||||
}
|
||||
}
|
||||
else if (st == OpenThermStatus::DELAY) {
|
||||
if ((newTs - ts) > 100000) {
|
||||
status = OpenThermStatus::READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenTherm::parity(unsigned long frame) //odd parity
|
||||
{
|
||||
byte p = 0;
|
||||
while (frame > 0)
|
||||
{
|
||||
if (frame & 1) p++;
|
||||
frame = frame >> 1;
|
||||
}
|
||||
return (p & 1);
|
||||
}
|
||||
|
||||
OpenThermMessageType OpenTherm::getMessageType(unsigned long message)
|
||||
{
|
||||
OpenThermMessageType msg_type = static_cast<OpenThermMessageType>((message >> 28) & 7);
|
||||
return msg_type;
|
||||
}
|
||||
|
||||
OpenThermMessageID OpenTherm::getDataID(unsigned long frame)
|
||||
{
|
||||
return (OpenThermMessageID)((frame >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data)
|
||||
{
|
||||
unsigned long request = data;
|
||||
if (type == OpenThermMessageType::WRITE_DATA) {
|
||||
request |= 1ul << 28;
|
||||
}
|
||||
request |= ((unsigned long)id) << 16;
|
||||
if (OpenTherm::parity(request)) request |= (1ul << 31);
|
||||
return request;
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data)
|
||||
{
|
||||
unsigned long response = data;
|
||||
response |= type << 28;
|
||||
response |= ((unsigned long)id) << 16;
|
||||
if (OpenTherm::parity(response)) response |= (1ul << 31);
|
||||
return response;
|
||||
}
|
||||
|
||||
bool OpenTherm::isValidResponse(unsigned long response)
|
||||
{
|
||||
if (OpenTherm::parity(response)) return false;
|
||||
byte msgType = (response << 1) >> 29;
|
||||
return msgType == READ_ACK || msgType == WRITE_ACK;
|
||||
}
|
||||
|
||||
bool OpenTherm::isValidRequest(unsigned long request)
|
||||
{
|
||||
if (OpenTherm::parity(request)) return false;
|
||||
byte msgType = (request << 1) >> 29;
|
||||
return msgType == READ_DATA || msgType == WRITE_DATA;
|
||||
}
|
||||
|
||||
void OpenTherm::end() {
|
||||
if (this->handleInterruptCallback != NULL) {
|
||||
detachInterrupt(digitalPinToInterrupt(inPin));
|
||||
}
|
||||
}
|
||||
|
||||
const char *OpenTherm::statusToString(OpenThermResponseStatus status)
|
||||
{
|
||||
switch (status) {
|
||||
case NONE: return "NONE";
|
||||
case SUCCESS: return "SUCCESS";
|
||||
case INVALID: return "INVALID";
|
||||
case TIMEOUT: return "TIMEOUT";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *OpenTherm::messageTypeToString(OpenThermMessageType message_type)
|
||||
{
|
||||
switch (message_type) {
|
||||
case READ_DATA: return "READ_DATA";
|
||||
case WRITE_DATA: return "WRITE_DATA";
|
||||
case INVALID_DATA: return "INVALID_DATA";
|
||||
case RESERVED: return "RESERVED";
|
||||
case READ_ACK: return "READ_ACK";
|
||||
case WRITE_ACK: return "WRITE_ACK";
|
||||
case DATA_INVALID: return "DATA_INVALID";
|
||||
case UNKNOWN_DATA_ID: return "UNKNOWN_DATA_ID";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
//building requests
|
||||
|
||||
unsigned long OpenTherm::buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2) {
|
||||
unsigned int data = enableCentralHeating | (enableHotWater << 1) | (enableCooling << 2) | (enableOutsideTemperatureCompensation << 3) | (enableCentralHeating2 << 4);
|
||||
data <<= 8;
|
||||
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Status, data);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildSetBoilerTemperatureRequest(float temperature) {
|
||||
unsigned int data = temperatureToData(temperature);
|
||||
return buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildSetHotWaterTemperatureRequest(float temperature) {
|
||||
unsigned int data = temperatureToData(temperature);
|
||||
return buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildGetBoilerTemperatureRequest() {
|
||||
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::Tboiler, 0);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::buildSlaveConfigurationRequest() {
|
||||
return buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0);
|
||||
}
|
||||
|
||||
//parsing responses
|
||||
bool OpenTherm::isFault(unsigned long response) {
|
||||
return response & 0x1;
|
||||
}
|
||||
|
||||
bool OpenTherm::isCentralHeatingActive(unsigned long response) {
|
||||
return response & 0x2;
|
||||
}
|
||||
|
||||
bool OpenTherm::isHotWaterActive(unsigned long response) {
|
||||
return response & 0x4;
|
||||
}
|
||||
|
||||
bool OpenTherm::isFlameOn(unsigned long response) {
|
||||
return response & 0x8;
|
||||
}
|
||||
|
||||
bool OpenTherm::isCoolingActive(unsigned long response) {
|
||||
return response & 0x10;
|
||||
}
|
||||
|
||||
bool OpenTherm::isDiagnostic(unsigned long response) {
|
||||
return response & 0x40;
|
||||
}
|
||||
|
||||
uint16_t OpenTherm::getUInt(const unsigned long response) {
|
||||
const uint16_t u88 = response & 0xffff;
|
||||
return u88;
|
||||
}
|
||||
|
||||
float OpenTherm::getFloat(const unsigned long response) {
|
||||
const uint16_t u88 = getUInt(response);
|
||||
const float f = (u88 & 0x8000) ? -(0x10000L - u88) / 256.0f : u88 / 256.0f;
|
||||
return f;
|
||||
}
|
||||
|
||||
unsigned int OpenTherm::temperatureToData(float temperature) {
|
||||
if (temperature < 0) temperature = 0;
|
||||
if (temperature > 100) temperature = 100;
|
||||
unsigned int data = (unsigned int)(temperature * 256);
|
||||
return data;
|
||||
}
|
||||
|
||||
//basic requests
|
||||
|
||||
unsigned long OpenTherm::setBoilerStatus(bool enableCentralHeating, bool enableHotWater, bool enableCooling, bool enableOutsideTemperatureCompensation, bool enableCentralHeating2) {
|
||||
return sendRequest(buildSetBoilerStatusRequest(enableCentralHeating, enableHotWater, enableCooling, enableOutsideTemperatureCompensation, enableCentralHeating2));
|
||||
}
|
||||
|
||||
bool OpenTherm::setBoilerTemperature(float temperature) {
|
||||
unsigned long response = sendRequest(buildSetBoilerTemperatureRequest(temperature));
|
||||
return isValidResponse(response);
|
||||
}
|
||||
|
||||
bool OpenTherm::setHotWaterTemperature(float temperature) {
|
||||
unsigned long response = sendRequest(buildSetHotWaterTemperatureRequest(temperature));
|
||||
return isValidResponse(response);
|
||||
}
|
||||
|
||||
float OpenTherm::getBoilerTemperature() {
|
||||
unsigned long response = sendRequest(buildGetBoilerTemperatureRequest());
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
float OpenTherm::getReturnTemperature() {
|
||||
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tret, 0));
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
float OpenTherm::getModulation() {
|
||||
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::RelModLevel, 0));
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
float OpenTherm::getPressure() {
|
||||
unsigned long response = sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::CHPressure, 0));
|
||||
return isValidResponse(response) ? getFloat(response) : 0;
|
||||
}
|
||||
|
||||
unsigned char OpenTherm::getFault() {
|
||||
return ((sendRequest(buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0)) >> 8) & 0xff);
|
||||
}
|
||||
|
||||
unsigned long OpenTherm::getSlaveConfiguration() {
|
||||
return sendRequest(buildSlaveConfigurationRequest());
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
OpenTherm.h - OpenTherm Library for the ESP8266/Arduino platform
|
||||
https://github.com/ihormelnyk/OpenTherm
|
||||
http://ihormelnyk.com/pages/OpenTherm
|
||||
Licensed under MIT license
|
||||
Copyright 2018, Ihor Melnyk
|
||||
|
||||
Frame Structure:
|
||||
P MGS-TYPE SPARE DATA-ID DATA-VALUE
|
||||
0 000 0000 00000000 00000000 00000000
|
||||
*/
|
||||
|
||||
#ifndef OpenTherm_h
|
||||
#define OpenTherm_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
enum OpenThermResponseStatus {
|
||||
NONE,
|
||||
SUCCESS,
|
||||
INVALID,
|
||||
TIMEOUT
|
||||
};
|
||||
|
||||
|
||||
enum OpenThermMessageType {
|
||||
/* Master to Slave */
|
||||
READ_DATA = B000,
|
||||
READ = READ_DATA, // for backwared compatibility
|
||||
WRITE_DATA = B001,
|
||||
WRITE = WRITE_DATA, // for backwared compatibility
|
||||
INVALID_DATA = B010,
|
||||
RESERVED = B011,
|
||||
/* Slave to Master */
|
||||
READ_ACK = B100,
|
||||
WRITE_ACK = B101,
|
||||
DATA_INVALID = B110,
|
||||
UNKNOWN_DATA_ID = B111
|
||||
};
|
||||
|
||||
typedef OpenThermMessageType OpenThermRequestType; // for backwared compatibility
|
||||
|
||||
enum OpenThermMessageID {
|
||||
Status, // flag8 / flag8 Master and Slave Status flags.
|
||||
TSet, // f8.8 Control setpoint ie CH water temperature setpoint (°C)
|
||||
MConfigMMemberIDcode, // flag8 / u8 Master Configuration Flags / Master MemberID Code
|
||||
SConfigSMemberIDcode, // flag8 / u8 Slave Configuration Flags / Slave MemberID Code
|
||||
Command, // u8 / u8 Remote Command
|
||||
ASFflags, // / OEM-fault-code flag8 / u8 Application-specific fault flags and OEM fault code
|
||||
RBPflags, // flag8 / flag8 Remote boiler parameter transfer-enable & read/write flags
|
||||
CoolingControl, // f8.8 Cooling control signal (%)
|
||||
TsetCH2, // f8.8 Control setpoint for 2e CH circuit (°C)
|
||||
TrOverride, // f8.8 Remote override room setpoint
|
||||
TSP, // u8 / u8 Number of Transparent-Slave-Parameters supported by slave
|
||||
TSPindexTSPvalue, // u8 / u8 Index number / Value of referred-to transparent slave parameter.
|
||||
FHBsize, // u8 / u8 Size of Fault-History-Buffer supported by slave
|
||||
FHBindexFHBvalue, // u8 / u8 Index number / Value of referred-to fault-history buffer entry.
|
||||
MaxRelModLevelSetting, // f8.8 Maximum relative modulation level setting (%)
|
||||
MaxCapacityMinModLevel, // u8 / u8 Maximum boiler capacity (kW) / Minimum boiler modulation level(%)
|
||||
TrSet, // f8.8 Room Setpoint (°C)
|
||||
RelModLevel, // f8.8 Relative Modulation Level (%)
|
||||
CHPressure, // f8.8 Water pressure in CH circuit (bar)
|
||||
DHWFlowRate, // f8.8 Water flow rate in DHW circuit. (litres/minute)
|
||||
DayTime, // special / u8 Day of Week and Time of Day
|
||||
Date, // u8 / u8 Calendar date
|
||||
Year, // u16 Calendar year
|
||||
TrSetCH2, // f8.8 Room Setpoint for 2nd CH circuit (°C)
|
||||
Tr, // f8.8 Room temperature (°C)
|
||||
Tboiler, // f8.8 Boiler flow water temperature (°C)
|
||||
Tdhw, // f8.8 DHW temperature (°C)
|
||||
Toutside, // f8.8 Outside temperature (°C)
|
||||
Tret, // f8.8 Return water temperature (°C)
|
||||
Tstorage, // f8.8 Solar storage temperature (°C)
|
||||
Tcollector, // f8.8 Solar collector temperature (°C)
|
||||
TflowCH2, // f8.8 Flow water temperature CH2 circuit (°C)
|
||||
Tdhw2, // f8.8 Domestic hot water temperature 2 (°C)
|
||||
Texhaust, // s16 Boiler exhaust temperature (°C)
|
||||
TdhwSetUBTdhwSetLB = 48, // s8 / s8 DHW setpoint upper & lower bounds for adjustment (°C)
|
||||
MaxTSetUBMaxTSetLB, // s8 / s8 Max CH water setpoint upper & lower bounds for adjustment (°C)
|
||||
HcratioUBHcratioLB, // s8 / s8 OTC heat curve ratio upper & lower bounds for adjustment
|
||||
TdhwSet = 56, // f8.8 DHW setpoint (°C) (Remote parameter 1)
|
||||
MaxTSet, // f8.8 Max CH water setpoint (°C) (Remote parameters 2)
|
||||
Hcratio, // f8.8 OTC heat curve ratio (°C) (Remote parameter 3)
|
||||
RemoteOverrideFunction = 100, // flag8 / - Function of manual and program changes in master and remote room setpoint.
|
||||
OEMDiagnosticCode = 115, // u16 OEM-specific diagnostic/service code
|
||||
BurnerStarts, // u16 Number of starts burner
|
||||
CHPumpStarts, // u16 Number of starts CH pump
|
||||
DHWPumpValveStarts, // u16 Number of starts DHW pump/valve
|
||||
DHWBurnerStarts, // u16 Number of starts burner during DHW mode
|
||||
BurnerOperationHours, // u16 Number of hours that burner is in operation (i.e. flame on)
|
||||
CHPumpOperationHours, // u16 Number of hours that CH pump has been running
|
||||
DHWPumpValveOperationHours, // u16 Number of hours that DHW pump has been running or DHW valve has been opened
|
||||
DHWBurnerOperationHours, // u16 Number of hours that burner is in operation during DHW mode
|
||||
OpenThermVersionMaster, // f8.8 The implemented version of the OpenTherm Protocol Specification in the master.
|
||||
OpenThermVersionSlave, // f8.8 The implemented version of the OpenTherm Protocol Specification in the slave.
|
||||
MasterVersion, // u8 / u8 Master product version number and type
|
||||
SlaveVersion, // u8 / u8 Slave product version number and type
|
||||
};
|
||||
|
||||
enum OpenThermStatus {
|
||||
NOT_INITIALIZED,
|
||||
READY,
|
||||
DELAY,
|
||||
REQUEST_SENDING,
|
||||
RESPONSE_WAITING,
|
||||
RESPONSE_START_BIT,
|
||||
RESPONSE_RECEIVING,
|
||||
RESPONSE_READY,
|
||||
RESPONSE_INVALID
|
||||
};
|
||||
|
||||
class OpenTherm
|
||||
{
|
||||
public:
|
||||
OpenTherm(int inPin = 4, int outPin = 5, bool isSlave = false);
|
||||
volatile OpenThermStatus status;
|
||||
void begin(void(*handleInterruptCallback)(void));
|
||||
void begin(void(*handleInterruptCallback)(void), void(*processResponseCallback)(unsigned long, int));
|
||||
bool isReady();
|
||||
unsigned long sendRequest(unsigned long request);
|
||||
bool sendResponse(unsigned long request);
|
||||
bool sendRequestAync(unsigned long request);
|
||||
static unsigned long buildRequest(OpenThermMessageType type, OpenThermMessageID id, unsigned int data);
|
||||
static unsigned long buildResponse(OpenThermMessageType type, OpenThermMessageID id, unsigned int data);
|
||||
OpenThermResponseStatus getLastResponseStatus();
|
||||
const char *statusToString(OpenThermResponseStatus status);
|
||||
void handleInterrupt();
|
||||
void process();
|
||||
void end();
|
||||
|
||||
static bool parity(unsigned long frame);
|
||||
OpenThermMessageType getMessageType(unsigned long message);
|
||||
OpenThermMessageID getDataID(unsigned long frame);
|
||||
const char *messageTypeToString(OpenThermMessageType message_type);
|
||||
bool isValidRequest(unsigned long request);
|
||||
bool isValidResponse(unsigned long response);
|
||||
|
||||
//requests
|
||||
unsigned long buildSetBoilerStatusRequest(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false);
|
||||
unsigned long buildSetBoilerTemperatureRequest(float temperature);
|
||||
unsigned long buildGetBoilerTemperatureRequest();
|
||||
unsigned long buildSetHotWaterTemperatureRequest(float temperature);
|
||||
unsigned long buildSlaveConfigurationRequest();
|
||||
|
||||
|
||||
//responses
|
||||
static bool isFault(unsigned long response);
|
||||
static bool isCentralHeatingActive(unsigned long response);
|
||||
static bool isHotWaterActive(unsigned long response);
|
||||
static bool isFlameOn(unsigned long response);
|
||||
static bool isCoolingActive(unsigned long response);
|
||||
static bool isDiagnostic(unsigned long response);
|
||||
static uint16_t getUInt(const unsigned long response);
|
||||
static float getFloat(const unsigned long response);
|
||||
static unsigned int temperatureToData(float temperature);
|
||||
|
||||
//basic requests
|
||||
unsigned long setBoilerStatus(bool enableCentralHeating, bool enableHotWater = false, bool enableCooling = false, bool enableOutsideTemperatureCompensation = false, bool enableCentralHeating2 = false);
|
||||
bool setBoilerTemperature(float temperature);
|
||||
bool setHotWaterTemperature(float temperature);
|
||||
float getBoilerTemperature();
|
||||
float getReturnTemperature();
|
||||
float getModulation();
|
||||
float getPressure();
|
||||
unsigned char getFault();
|
||||
unsigned long getSlaveConfiguration();
|
||||
|
||||
private:
|
||||
const int inPin;
|
||||
const int outPin;
|
||||
const bool isSlave;
|
||||
|
||||
volatile unsigned long response;
|
||||
volatile OpenThermResponseStatus responseStatus;
|
||||
volatile unsigned long responseTimestamp;
|
||||
volatile byte responseBitIndex;
|
||||
|
||||
int readState();
|
||||
void setActiveState();
|
||||
void setIdleState();
|
||||
void activateBoiler();
|
||||
|
||||
void sendBit(bool high);
|
||||
void(*handleInterruptCallback)();
|
||||
void(*processResponseCallback)(unsigned long, int);
|
||||
};
|
||||
|
||||
#ifndef ICACHE_RAM_ATTR
|
||||
#define ICACHE_RAM_ATTR
|
||||
#endif
|
||||
|
||||
#endif // OpenTherm_h
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Частици"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Жест"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Грийн"
|
||||
#define D_COLOR_BLUE "син"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "близост"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Ускорение - ос X"
|
||||
#define D_AY_AXIS "Ускорение - ос Y"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_BG_BG_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "částic"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesto"
|
||||
#define D_COLOR_RED "Červená"
|
||||
#define D_COLOR_GREEN "Zelená"
|
||||
#define D_COLOR_BLUE "Modrá"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Blízkost"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. osa-X"
|
||||
#define D_AY_AXIS "Accel. osa-Y"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_CS_CZ_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partikel"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Geste"
|
||||
#define D_COLOR_RED "Rot"
|
||||
#define D_COLOR_GREEN "Grün"
|
||||
#define D_COLOR_BLUE "Blau"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Nähe"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Beschl. X-Achse"
|
||||
#define D_AY_AXIS "Beschl. Y-Achse"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "Kalibrierung fehlerhaft"
|
||||
#define D_AS3935_CAL_OK "Cap gesetzt auf:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_DE_DE_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particals"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Χειρονομία"
|
||||
#define D_COLOR_RED "Κόκκινο"
|
||||
#define D_COLOR_GREEN "Πράσινο"
|
||||
#define D_COLOR_BLUE "Μπλε"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Εγγύτητα"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_EL_GR_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particles"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -688,6 +696,7 @@
|
|||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "°K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
|
@ -786,4 +795,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_EN_GB_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partículas"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesto"
|
||||
#define D_COLOR_RED "Rojo"
|
||||
#define D_COLOR_GREEN "Verde"
|
||||
#define D_COLOR_BLUE "Azul"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximidad"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_ES_ES_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particules"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Geste"
|
||||
#define D_COLOR_RED "Rouge"
|
||||
#define D_COLOR_GREEN "Vert"
|
||||
#define D_COLOR_BLUE "Bleu"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximité"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accél. Axe-X"
|
||||
#define D_AY_AXIS "Accél. Axe-Y"
|
||||
|
@ -503,7 +511,7 @@
|
|||
#define D_CALIBRATE "Étalonner"
|
||||
#define D_CALIBRATION "Étalonnage"
|
||||
|
||||
//xsns_35_TX20.ino
|
||||
// xsns_35_TX20.ino
|
||||
#define D_TX20_WIND_DIRECTION "Direction du vent"
|
||||
#define D_TX20_WIND_SPEED "Vitesse du vent"
|
||||
#define D_TX20_WIND_SPEED_MIN "Vitesse Min"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_FR_FR_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "חלקיקים"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_HE_HE_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Részecskék"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesztus"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "közelség"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Gyorsulásm. X-tengely"
|
||||
#define D_AY_AXIS "Gyorsulásm. Y-tengely"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_HU_HU_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particelle"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesto"
|
||||
#define D_COLOR_RED "Rosso"
|
||||
#define D_COLOR_GREEN "Verde"
|
||||
#define D_COLOR_BLUE "Blu"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Vicinanza"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accelerazione asse X"
|
||||
#define D_AY_AXIS "Accelerazione asse Y"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibrazione fallita"
|
||||
#define D_AS3935_CAL_OK "calibrazione impostata a:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_IT_IT_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "입자"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_KO_KO_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Stofdeeltjes"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Versn. X-as"
|
||||
#define D_AY_AXIS "Versn. Y-as"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_NL_NL_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Cząstki"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_PL_PL_D_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partículas"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_PT_BR_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partículas"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_PT_PT_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particule"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel.Axa-X"
|
||||
#define D_AY_AXIS "Accel.Axa-Y"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_RO_RO_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particals"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_RU_RU_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "častíc"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. os-X"
|
||||
#define D_AY_AXIS "Accel. os-Y"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_SK_SK_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Partiklar"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axel"
|
||||
#define D_AY_AXIS "Accel. Y-Axel"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_SV_SE_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Particals"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_TR_TR_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "Частинки понад"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Приск. Вісь-X"
|
||||
#define D_AY_AXIS "Приск. Вісь-Y"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_UK_UA_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "颗粒物直径大于"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "加速度计X轴分量"
|
||||
#define D_AY_AXIS "加速度计Y轴分量"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_ZH_CN_H_
|
||||
|
|
|
@ -482,6 +482,14 @@
|
|||
#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter
|
||||
#define D_PARTICALS_BEYOND "顆粒物直徑大於"
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Accel. X-Axis"
|
||||
#define D_AY_AXIS "Accel. Y-Axis"
|
||||
|
@ -786,4 +794,8 @@
|
|||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
|
||||
#endif // _LANGUAGE_ZH_TW_H_
|
||||
|
|
|
@ -478,6 +478,10 @@
|
|||
// #define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
|
||||
// #define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
|
||||
// #define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
|
||||
#define USE_APDS9960_GESTURE // Enable APDS9960 Gesture feature (+2k code)
|
||||
#define USE_APDS9960_PROXIMITY // Enable APDS9960 Proximity feature (>50 code)
|
||||
#define USE_APDS9960_COLOR // Enable APDS9960 Color feature (+0.8k code)
|
||||
#define USE_APDS9960_STARTMODE 0 // Default to enable Gesture mode
|
||||
// #define USE_MCP230xx // [I2cDriver22] Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code)
|
||||
// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x26 - set according to your wired setup)
|
||||
// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code)
|
||||
|
@ -664,6 +668,8 @@
|
|||
|
||||
//#define USE_WINDMETER // Add support for analog anemometer
|
||||
|
||||
//#define USE_OPENTHERM // Use OpenTherm implementation
|
||||
|
||||
// -- End of general directives -------------------
|
||||
|
||||
/*********************************************************************************************\
|
||||
|
|
|
@ -482,7 +482,10 @@ struct {
|
|||
uint8_t shutter_startrelay[MAX_SHUTTERS]; // E84
|
||||
uint8_t pcf8574_config[MAX_PCF8574]; // E88
|
||||
|
||||
uint8_t free_e8c[4]; // E8C
|
||||
uint8_t ot_hot_water_setpoint; // E8C
|
||||
uint8_t ot_boiler_setpoint; // E8D
|
||||
uint8_t ot_flags; // E8E
|
||||
uint8_t free_e8f[1]; // E8F
|
||||
|
||||
uint16_t dimmer_hw_min; // E90
|
||||
uint16_t dimmer_hw_max; // E92
|
||||
|
|
|
@ -554,12 +554,13 @@ void GetFeatures(void)
|
|||
#ifdef USE_PING
|
||||
feature6 |= 0x00000080; // xdrv_38_ping.ino
|
||||
#endif
|
||||
#ifdef USE_THERMOSTAT
|
||||
feature6 |= 0x00000100; // xsns_68_opentherm.ino
|
||||
#endif
|
||||
#ifdef USE_WINDMETER
|
||||
feature6 |= 0x00000100; // xsns_68_windmeter.ino
|
||||
feature6 |= 0x00000200; // xsns_69_windmeter.ino
|
||||
#endif
|
||||
|
||||
// feature6 |= 0x00000100;
|
||||
// feature6 |= 0x00000200;
|
||||
// feature6 |= 0x00000400;
|
||||
// feature6 |= 0x00000800;
|
||||
|
||||
|
|
|
@ -230,6 +230,8 @@ enum UserSelectablePins {
|
|||
GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX
|
||||
GPIO_AS3935,
|
||||
GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface
|
||||
GPIO_BOILER_OT_RX, // OpenTherm Boiler RX pin
|
||||
GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin
|
||||
GPIO_WINDMETER_SPEED, // WindMeter speed counter pin
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
|
@ -319,6 +321,7 @@ const char kSensorNames[] PROGMEM =
|
|||
D_SENSOR_HRXL_RX "|"
|
||||
D_SENSOR_ELECTRIQ_MOODL "|"
|
||||
D_SENSOR_AS3935 "|" D_SENSOR_PMS5003_TX "|"
|
||||
D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX "|"
|
||||
D_SENSOR_WINDMETER_SPEED
|
||||
;
|
||||
|
||||
|
@ -593,6 +596,9 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
|||
#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR)
|
||||
GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin
|
||||
#endif
|
||||
#ifdef USE_WINDMETER
|
||||
GPIO_WINDMETER_SPEED,
|
||||
#endif
|
||||
#ifdef USE_MP3_PLAYER
|
||||
GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface
|
||||
#endif
|
||||
|
@ -667,8 +673,9 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
|||
#ifdef USE_AS3935
|
||||
GPIO_AS3935,
|
||||
#endif
|
||||
#ifdef USE_WINDMETER
|
||||
GPIO_WINDMETER_SPEED,
|
||||
#ifdef USE_OPENTHERM
|
||||
GPIO_BOILER_OT_RX,
|
||||
GPIO_BOILER_OT_TX,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -125,6 +125,8 @@ enum UserSelectablePins {
|
|||
GPIO_WEBCAM_PSCLK,
|
||||
GPIO_WEBCAM_HSD,
|
||||
GPIO_WEBCAM_PSRCS,
|
||||
GPIO_BOILER_OT_RX, // OpenTherm Boiler RX pin
|
||||
GPIO_BOILER_OT_TX, // OpenTherm Boiler TX pin
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
enum ProgramSelectablePins {
|
||||
|
@ -211,7 +213,8 @@ const char kSensorNames[] PROGMEM =
|
|||
D_GPIO_WEBCAM_VSYNC "|" D_GPIO_WEBCAM_HREF "|" D_GPIO_WEBCAM_PCLK "|"
|
||||
D_GPIO_WEBCAM_PSCLK "|"
|
||||
D_GPIO_WEBCAM_HSD "|"
|
||||
D_GPIO_WEBCAM_PSRCS
|
||||
D_GPIO_WEBCAM_PSRCS "|"
|
||||
D_SENSOR_BOILER_OT_RX "|" D_SENSOR_BOILER_OT_TX
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
|
@ -548,6 +551,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
|||
|
||||
AGPIO(GPIO_WEBCAM_PSRCS),
|
||||
#endif
|
||||
#ifdef USE_OPENTHERM
|
||||
AGPIO(GPIO_BOILER_OT_RX),
|
||||
AGPIO(GPIO_BOILER_OT_TX),
|
||||
#endif
|
||||
};
|
||||
|
||||
//********************************************************************************************
|
||||
|
|
|
@ -1181,6 +1181,12 @@ chknext:
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef ESP32
|
||||
if (!strncmp(vname,"core",4)) {
|
||||
fvar=xPortGetCoreID();
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case 'd':
|
||||
if (!strncmp(vname,"day",3)) {
|
||||
|
@ -1705,6 +1711,12 @@ chknext:
|
|||
fvar=999;
|
||||
goto exit;
|
||||
}
|
||||
#ifdef ESP32
|
||||
if (!strncmp(vname,"pheap",5)) {
|
||||
fvar=ESP.getFreePsram();
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
if (!strncmp(vname,"prefix1",7)) {
|
||||
if (sp) strlcpy(sp,SettingsText(SET_MQTTPREFIX1),glob_script_mem.max_ssize);
|
||||
goto strexit;
|
||||
|
@ -5235,7 +5247,9 @@ bool Xdrv10(uint8_t function)
|
|||
break;
|
||||
#ifdef USE_SCRIPT_WEB_DISPLAY
|
||||
case FUNC_WEB_ADD_MAIN_BUTTON:
|
||||
ScriptWebShow('&');
|
||||
if (bitRead(Settings.rule_enabled, 0)) {
|
||||
ScriptWebShow('&');
|
||||
}
|
||||
break;
|
||||
#endif // USE_SCRIPT_WEB_DISPLAY
|
||||
case FUNC_WEB_ADD_HANDLER:
|
||||
|
|
|
@ -28,12 +28,14 @@ const char kHAssJsonSensorTypes[] PROGMEM =
|
|||
D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|"
|
||||
D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY "|"
|
||||
D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|";
|
||||
|
||||
const char kHAssJsonSensorUnits[] PROGMEM =
|
||||
"||||"
|
||||
"VA|%|A|Cm|Hz|%|LX|"
|
||||
"%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W|"
|
||||
"VAr|kWh|kWh|V|Kg|kWh|"
|
||||
"ppm|ppm|ppb|";
|
||||
|
||||
const char kHAssJsonSensorDevCla[] PROGMEM =
|
||||
"dev_cla\":\"temperature|ic\":\"mdi:weather-rainy|dev_cla\":\"pressure|dev_cla\":\"pressure|"
|
||||
"dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|"
|
||||
|
@ -74,12 +76,12 @@ const char HASS_DISCOVER_BIN_PIR[] PROGMEM =
|
|||
"\"pl_on\":\"%s\"," // ON
|
||||
"\"off_dly\":1"; // Switchmode13 and Switchmode14 doesn't transmit an OFF state.
|
||||
|
||||
const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM =
|
||||
const char HASS_DISCOVER_BASE_LIGHT[] PROGMEM =
|
||||
",\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer
|
||||
"\"bri_stat_t\":\"%s\"," // stat/led2/RESULT
|
||||
"\"bri_scl\":100," // 100%
|
||||
"\"on_cmd_type\":\"%s\"," // power on (first), power on (last), no power on (brightness)
|
||||
"\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\"";
|
||||
"\"bri_val_tpl\":\"{{value_json.%s}}\"";
|
||||
|
||||
const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM =
|
||||
",\"rgb_cmd_t\":\"%s2\"," // cmnd/led2/Color2
|
||||
|
@ -138,6 +140,16 @@ const char kHAssTriggerTypeButtons[] PROGMEM =
|
|||
const char kHAssTriggerStringButtons[] PROGMEM =
|
||||
"|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|HOLD|";
|
||||
|
||||
const char kHAssError1[] PROGMEM =
|
||||
"HASS: MQTT discovery failed due to too long topic or friendly name."
|
||||
"Please shorten topic and friendly name. Failed to format";
|
||||
|
||||
const char kHAssError2[] PROGMEM =
|
||||
"HASS: The configuration of the Relays is wrong, there is a Light that is using an index lower than the number of the selected relay.\n "
|
||||
"The Relays have priority over the Lights, an incorrect order could lead to an erroneous Light control.\n "
|
||||
"Please update your configuration to avoid inconsistent results.\n "
|
||||
"Entities for Relays and Lights will not be available in Home Assistant until the configuration will be updated.";
|
||||
|
||||
uint8_t hass_init_step = 0;
|
||||
uint8_t hass_mode = 0;
|
||||
int hass_tele_period = 0;
|
||||
|
@ -153,9 +165,7 @@ void TryResponseAppend_P(const char *format, ...)
|
|||
int slen = sizeof(mqtt_data) - 1 - mlen;
|
||||
if (dlen >= slen)
|
||||
{
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: MQTT discovery failed due to too long topic or friendly name. "
|
||||
"Please shorten topic and friendly name. Failed to format(%u/%u):"),
|
||||
dlen, slen);
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s (%u/%u):"), kHAssError1, dlen, slen);
|
||||
va_start(args, format);
|
||||
vsnprintf_P(log_data, sizeof(log_data), format, args);
|
||||
AddLog(LOG_LEVEL_ERROR);
|
||||
|
@ -175,13 +185,33 @@ void HAssAnnounceRelayLight(void)
|
|||
char stemp2[TOPSZ];
|
||||
char stemp3[TOPSZ];
|
||||
char unique_id[30];
|
||||
bool is_light = false;
|
||||
bool is_topic_light = false;
|
||||
|
||||
bool LightControl = light_controller.isCTRGBLinked(); // SetOption37 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels
|
||||
bool PwmMulti = Settings.flag3.pwm_multi_channels; // SetOption68 - Multi-channel PWM instead of a single light
|
||||
bool is_topic_light = false; // Switch HAss domain between Lights and Relays
|
||||
bool ind_light = false; // Controls Separated Lights when SetOption37 is >= 128
|
||||
bool ct_light = false; // Controls a CT Light when SetOption37 is >= 128
|
||||
bool wt_light = false; // Controls a White Light when SetOption37 is >= 128
|
||||
bool err_flag = false; // When true it blocks the creation of entities if the order of the Relays is not correct to avoid issue with Lights
|
||||
|
||||
uint8_t dimmer = 1;
|
||||
uint8_t max_lights = 1;
|
||||
|
||||
// If there is a special Light to be enabled and managed with SetOption68 or SetOption37 >= 128, Discovery calculates the maximum number of entities to be generated in advance
|
||||
|
||||
if (PwmMulti) { max_lights = Light.subtype; }
|
||||
|
||||
if (!LightControl) {
|
||||
ind_light = 1;
|
||||
if (!PwmMulti) { max_lights = 2;}
|
||||
}
|
||||
|
||||
// Lights types: 0 = LST_NONE / 1 = LST_SINGLE / 2 = LST_COLDWARM / 3 = LST_RGB / 4 = LST_RGBW / 5 = LST_RGBCW
|
||||
|
||||
for (uint32_t i = 1; i <= MAX_RELAYS; i++)
|
||||
{
|
||||
is_light = ((i == devices_present) && (light_type));
|
||||
is_topic_light = Settings.flag.hass_light || is_light; // SetOption30 - Enforce HAss autodiscovery as light
|
||||
bool RelayX = PinUsed(GPIO_REL1 +i-1);
|
||||
is_topic_light = Settings.flag.hass_light && RelayX || light_type && !RelayX; // SetOption30 - Enforce HAss autodiscovery as light
|
||||
|
||||
mqtt_data[0] = '\0'; // Clear retained message
|
||||
|
||||
|
@ -195,70 +225,95 @@ void HAssAnnounceRelayLight(void)
|
|||
snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"),
|
||||
(is_topic_light) ? "light" : "switch", unique_id);
|
||||
|
||||
if (Settings.flag.hass_discovery && (i <= devices_present))
|
||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||
char name[33 + 2]; // friendlyname(33) + " " + index
|
||||
char value_template[33];
|
||||
char prefix[TOPSZ];
|
||||
char *command_topic = stemp1;
|
||||
char *state_topic = stemp2;
|
||||
char *availability_topic = stemp3;
|
||||
if ((i < Light.device) && !RelayX) {
|
||||
err_flag = true;
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR("%s"), kHAssError2);
|
||||
} else {
|
||||
if (Settings.flag.hass_discovery && (RelayX || (Light.device > 0) && (max_lights > 0)) && !err_flag )
|
||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||
char name[33 + 2]; // friendlyname(33) + " " + index
|
||||
char value_template[33];
|
||||
char prefix[TOPSZ];
|
||||
char *command_topic = stemp1;
|
||||
char *state_topic = stemp2;
|
||||
char *availability_topic = stemp3;
|
||||
|
||||
if (i > MAX_FRIENDLYNAMES) {
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i);
|
||||
} else {
|
||||
snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i - 1));
|
||||
if (i > MAX_FRIENDLYNAMES) {
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i-1);
|
||||
} else {
|
||||
snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i-1));
|
||||
}
|
||||
|
||||
GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
|
||||
GetTopic_P(command_topic, CMND, mqtt_topic, value_template);
|
||||
GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2));
|
||||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId());
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
if ((i >= Light.device)
|
||||
#ifdef ESP8266
|
||||
|| PWM_DIMMER == my_module_type
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (!RelayX) {
|
||||
char *brightness_command_topic = stemp1;
|
||||
strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes
|
||||
char channel_num[9];
|
||||
if (PwmMulti) { // SetOption68 - Multi-channel PWM instead of a single light
|
||||
snprintf_P(channel_num, sizeof(channel_num), PSTR("Channel%d"), i);
|
||||
} else {
|
||||
if (!LightControl) { // SetOption37 >= 128 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels
|
||||
snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER "%d"), dimmer);
|
||||
dimmer ++;
|
||||
} else {
|
||||
snprintf_P(channel_num, sizeof(channel_num), PSTR("" D_CMND_DIMMER ""));
|
||||
}
|
||||
}
|
||||
GetTopic_P(brightness_command_topic, CMND, mqtt_topic, channel_num);
|
||||
TryResponseAppend_P(HASS_DISCOVER_BASE_LIGHT, brightness_command_topic, state_topic, stemp3, channel_num);
|
||||
}
|
||||
if ((ind_light && !PwmMulti) || LightControl) {
|
||||
|
||||
if (Light.subtype >= LST_RGB) {
|
||||
char *rgb_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic);
|
||||
|
||||
char *effect_command_topic = stemp1;
|
||||
GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic);
|
||||
}
|
||||
if (LST_RGBW == Light.subtype) { wt_light = true; }
|
||||
if (LST_RGBCW == Light.subtype) { ct_light = true; }
|
||||
}
|
||||
|
||||
if ((!ind_light && ct_light) || (LST_COLDWARM == Light.subtype &&
|
||||
!PwmMulti && LightControl)) {
|
||||
char *color_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic);
|
||||
ct_light = false;
|
||||
}
|
||||
if ((!ind_light && wt_light) || (LST_RGBW == Light.subtype &&
|
||||
!PwmMulti && LightControl)) {
|
||||
char *white_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic);
|
||||
wt_light = false;
|
||||
}
|
||||
ind_light = false;
|
||||
max_lights--;
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
TryResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
|
||||
GetTopic_P(command_topic, CMND, mqtt_topic, value_template);
|
||||
GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2));
|
||||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId());
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
if (is_light
|
||||
#ifdef ESP8266
|
||||
|| PWM_DIMMER == my_module_type
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char *brightness_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER);
|
||||
strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3);
|
||||
|
||||
if (Light.subtype >= LST_RGB)
|
||||
{
|
||||
char *rgb_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic);
|
||||
|
||||
char *effect_command_topic = stemp1;
|
||||
GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic);
|
||||
}
|
||||
if (LST_RGBW == Light.subtype)
|
||||
{
|
||||
char *white_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic);
|
||||
}
|
||||
if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype))
|
||||
{
|
||||
char *color_temp_command_topic = stemp1;
|
||||
|
||||
GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE);
|
||||
TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic);
|
||||
}
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
TryResponseAppend_P(PSTR("}"));
|
||||
}
|
||||
MqttPublish(stopic, true);
|
||||
}
|
||||
|
@ -346,7 +401,7 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint
|
|||
GetTopic_P(state_topic, STAT, mqtt_topic, jsoname);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
|
||||
snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), SettingsText(SET_FRIENDLYNAME1), device + 1);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), ModuleName().c_str(), device + 1);
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
if (!pir) {
|
||||
TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1));
|
||||
|
@ -372,7 +427,7 @@ void HAssAnnounceSwitches(void)
|
|||
|
||||
if (PinUsed(GPIO_SWT1, switch_index)) { switch_present = 1; }
|
||||
|
||||
if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) // Enable Discovery for Switches only if Switchtopic is set to a custom name
|
||||
if (KeyTopicActive(1) && strcmp(SettingsText(SET_MQTT_SWITCH_TOPIC), mqtt_topic)) // Enable Discovery for Switches only if SwitchTopic is set to a custom name
|
||||
{
|
||||
|
||||
// switch matrix for triggers and binary sensor generation when switchtopic is set as custom (default index is 0,0 - TOGGLE, TOGGLE):
|
||||
|
@ -436,7 +491,6 @@ void HAssAnnounceSwitches(void)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void HAssAnnounceButtons(void)
|
||||
{
|
||||
for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++)
|
||||
|
@ -469,7 +523,7 @@ void HAssAnnounceButtons(void)
|
|||
|
||||
// Trigger types: 10 = button_short_press | 11 = button_double_press | 12 = button_triple_press | 13 = button_quadruple_press | 14 = button_quintuple_press | 3 = button_long_press
|
||||
|
||||
if (!Settings.flag3.mqtt_buttons) { // Enable buttons discovery [SetOption73] - Decouple button from relay and send just mqtt topic
|
||||
if (!Settings.flag3.mqtt_buttons) { // Enable Buttons for discovery [SetOption73] - Decouple button from relay and send just mqtt topic
|
||||
button_present = 0;
|
||||
} else {
|
||||
if (Settings.flag.button_single) { // [SetOption13] Immediate action on button press, just SINGLE trigger
|
||||
|
@ -504,7 +558,7 @@ void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const
|
|||
char *availability_topic = stemp2;
|
||||
|
||||
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR));
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, MultiSubName);
|
||||
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), ModuleName().c_str(), sensorname, MultiSubName);
|
||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||
|
||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||
|
@ -575,7 +629,6 @@ void HAssAnnounceSensors(void)
|
|||
snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); // {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}}
|
||||
// USE THE FOLLOWING LINE TO TEST JSON
|
||||
//snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"HX711\":{\"Weight\":[22,34,1023.4]}}"));
|
||||
//snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"TX23\":{\"Speed\":{\"Act\":8.6,\"Avg\":8.2,\"Min\":0,\"Max\":15.8},\"Dir\":{\"Card\":\"SSO\",\"Deg\":157.5,\"Avg\":145.5,\"AvgCard\":\"SO\",\"Min\":112.5,\"Max\":292.5,\"Range\":180}}}"));
|
||||
|
||||
StaticJsonBuffer<500> jsonBuffer;
|
||||
JsonObject &root = jsonBuffer.parseObject(sensordata);
|
||||
|
@ -659,16 +712,13 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void)
|
|||
|
||||
void HAssPublishStatus(void)
|
||||
{
|
||||
Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\","
|
||||
"\"" D_JSON_COREVERSION "\":\"" ARDUINO_CORE_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
|
||||
"\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\","
|
||||
"\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,"
|
||||
"\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\","
|
||||
"\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"),
|
||||
my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(),
|
||||
GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(),
|
||||
Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(),
|
||||
WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg);
|
||||
Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_CMND_MODULE " or " D_CMND_TEMPLATE"\":\"%s\","
|
||||
"\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\","
|
||||
"\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_RSSI "\":\"%d\",\"" D_JSON_SIGNAL " (dBm)""\":\"%d\","
|
||||
"\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"LoadAvg\":%lu}"),
|
||||
my_version, my_image, GetBuildDateAndTime().c_str(), ModuleName().c_str(), GetResetReason().c_str(),
|
||||
GetUptime().c_str(), my_hostname, WiFi.localIP().toString().c_str(), WifiGetRssiAsQuality(WiFi.RSSI()),
|
||||
WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), loop_load_avg);
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE));
|
||||
}
|
||||
|
||||
|
@ -687,8 +737,6 @@ void HAssDiscovery(void)
|
|||
|
||||
if (Settings.flag.hass_discovery || (1 == hass_mode))
|
||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||
// Send info about relays and lights
|
||||
HAssAnnounceRelayLight();
|
||||
|
||||
// Send info about buttons
|
||||
HAssAnnounceButtons();
|
||||
|
@ -699,6 +747,9 @@ void HAssDiscovery(void)
|
|||
// Send info about sensors
|
||||
HAssAnnounceSensors();
|
||||
|
||||
// Send info about relays and lights
|
||||
HAssAnnounceRelayLight();
|
||||
|
||||
// Send info about status sensor
|
||||
HAssAnnounceDeviceInfoAndStatusSensor();
|
||||
}
|
||||
|
|
|
@ -878,13 +878,13 @@ bool Xdrv39(uint8_t function) {
|
|||
wc_pic_setup();
|
||||
break;
|
||||
case FUNC_WEB_ADD_MAIN_BUTTON:
|
||||
//if (Settings.esp32_webcam_resolution) {
|
||||
#ifndef USE_SCRIPT
|
||||
if (Settings.esp32_webcam_resolution) {
|
||||
//#ifndef USE_SCRIPT
|
||||
WcStreamControl(Settings.esp32_webcam_resolution);
|
||||
delay(50); // Give the webcam webserver some time to prepare the stream
|
||||
wc_show_stream();
|
||||
#endif
|
||||
//}
|
||||
//#endif
|
||||
}
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kWCCommands, WCCommand);
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
// #undef USE_TSL2561 // possible address conflict on the I2C-bus
|
||||
// #endif
|
||||
// #endif
|
||||
|
||||
#define XSNS_27 27
|
||||
#define XI2C_21 21 // See I2CDEVICES.md
|
||||
|
||||
|
@ -61,17 +62,9 @@
|
|||
#define APDS9930_CHIPID_1 0x12 // we will check, if someone got an incorrect sensor
|
||||
#define APDS9930_CHIPID_2 0x39 // there are case reports about "accidentially bought" 9930's
|
||||
|
||||
|
||||
// TODO() : Move to my_user_config.h file
|
||||
#define USE_APDS9960_GESTURE // Enable Gesture feature (+2k code)
|
||||
#define USE_APDS9960_PROXIMITY // Enable Proximity feature (>50 code)
|
||||
#define USE_APDS9960_COLOR // Enable Color feature (+0.8k code)
|
||||
|
||||
#define APDS9960_MODE_GESTURE 0
|
||||
#define APDS9960_MODE_COLOR 1
|
||||
|
||||
#define USE_APDS9960_STARTMODE APDS9960_MODE_GESTURE
|
||||
|
||||
/* Gesture parameters */
|
||||
#define GESTURE_THRESHOLD_OUT 10
|
||||
#define GESTURE_SENSITIVITY_1 50
|
||||
|
@ -80,19 +73,8 @@
|
|||
#define APDS9960_LONG_RECOVERY 50 // long pause after sensor overload in loops
|
||||
#define APDS9960_MAX_GESTURE_CYCLES 50 // how many FIFO-reads are allowed to prevent crash
|
||||
|
||||
|
||||
// TODO() : Move to Translate file
|
||||
#define D_GESTURE "Gesture"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Green"
|
||||
#define D_COLOR_BLUE "Blue"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "Proximity"
|
||||
|
||||
#define D_UNIT_KELVIN "°K"
|
||||
|
||||
/******************************************************************************\
|
||||
* constants
|
||||
* Constants
|
||||
\******************************************************************************/
|
||||
|
||||
const char APDS9960_TAG[] PROGMEM = "APDS9960"; // Only one actualy
|
||||
|
|
|
@ -0,0 +1,598 @@
|
|||
/*
|
||||
xsns_68_opentherm.ino - OpenTherm protocol support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Yuriy Sannikov
|
||||
|
||||
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, either 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 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 <OpenTherm.h>
|
||||
|
||||
#ifdef USE_OPENTHERM
|
||||
|
||||
#define XSNS_68 68
|
||||
|
||||
// Hot water and boiler parameter ranges
|
||||
#define OT_HOT_WATER_MIN 23
|
||||
#define OT_HOT_WATER_MAX 55
|
||||
#define OT_BOILER_MIN 40
|
||||
#define OT_BOILER_MAX 85
|
||||
|
||||
#define OT_HOT_WATER_DEFAULT 36;
|
||||
#define OT_BOILER_DEFAULT 85;
|
||||
|
||||
// Seconds before OT will make an attempt to connect to the boiler after connection error
|
||||
#define SNS_OT_DISCONNECT_COOLDOWN_SECONDS 10
|
||||
|
||||
// Count of the OpenThermSettingsFlags
|
||||
#define OT_FLAGS_COUNT 6
|
||||
enum OpenThermSettingsFlags
|
||||
{
|
||||
// If set, central heating on/off state follows diagnostic indication bit(6), however
|
||||
// EnableCentralHeating flag has a priority over it
|
||||
EnableCentralHeatingOnDiagnostics = 0x01,
|
||||
// If set, DHW is on after restart.
|
||||
EnableHotWater = 0x02,
|
||||
// If set, keep CH always on after restart. If off, follows the EnableCentralHeatingOnDiagnostics rule
|
||||
EnableCentralHeating = 0x04,
|
||||
EnableCooling = 0x08,
|
||||
EnableTemperatureCompensation = 0x10,
|
||||
EnableCentralHeating2 = 0x20,
|
||||
};
|
||||
|
||||
enum OpenThermConnectionStatus
|
||||
{
|
||||
OTC_NONE, // OT not initialized
|
||||
OTC_DISCONNECTED, // OT communication timed out
|
||||
OTC_CONNECTING, // Connecting after start or from DISCONNECTED state
|
||||
OTC_HANDSHAKE, // Wait for the handshake response
|
||||
OTC_READY, // Last Known Good response state is SUCCESS and no requests are in flight
|
||||
OTC_INFLIGHT // Request sent, waiting from the response
|
||||
};
|
||||
|
||||
OpenThermConnectionStatus sns_ot_connection_status = OpenThermConnectionStatus::OTC_NONE;
|
||||
uint8_t sns_ot_disconnect_cooldown = 0;
|
||||
|
||||
OpenTherm *sns_ot_master = NULL;
|
||||
|
||||
// Has valid values if connection status is READY or INFLIGHT
|
||||
typedef struct OT_BOILER_STATUS_T
|
||||
{
|
||||
// Boiler fault code
|
||||
uint8_t m_fault_code;
|
||||
// Boiler OEM fault code
|
||||
uint8_t m_oem_fault_code;
|
||||
// Boilder OEM Diagnostics code
|
||||
uint16_t m_oem_diag_code;
|
||||
// OpenTherm ID(3) response.
|
||||
uint8_t m_slave_flags;
|
||||
// OpenTherm ID(1) codes. Should be used to display state
|
||||
unsigned long m_slave_raw_status;
|
||||
// Desired boiler states
|
||||
bool m_enableCentralHeating;
|
||||
bool m_enableHotWater;
|
||||
bool m_enableCooling;
|
||||
bool m_enableOutsideTemperatureCompensation;
|
||||
bool m_enableCentralHeating2;
|
||||
|
||||
// Some boilers has an input for the heat request. When short, heat is requested
|
||||
// OT ID(0) bit 6 may indicate state of the Heat Request input
|
||||
// By enabling this bit we will set m_enableCentralHeating to true when OT ID(0) bit 6 is set.
|
||||
// This enables to use external mechanical thermostat to enable heating.
|
||||
// Some of the use cases might be setting an emergency temperature to prevent freezing
|
||||
// in case of the software thermostat failure.
|
||||
bool m_useDiagnosticIndicationAsHeatRequest;
|
||||
|
||||
// Hot Water temperature
|
||||
float m_hotWaterSetpoint_read;
|
||||
// Flame Modulation
|
||||
float m_flame_modulation_read;
|
||||
// Boiler Temperature
|
||||
float m_boiler_temperature_read;
|
||||
|
||||
// Boiler desired values
|
||||
float m_boilerSetpoint;
|
||||
float m_hotWaterSetpoint;
|
||||
|
||||
} OT_BOILER_STATUS;
|
||||
|
||||
OT_BOILER_STATUS sns_ot_boiler_status;
|
||||
|
||||
const char *sns_opentherm_connection_stat_to_str(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case OpenThermConnectionStatus::OTC_NONE:
|
||||
return "NONE";
|
||||
case OpenThermConnectionStatus::OTC_DISCONNECTED:
|
||||
return "FAULT";
|
||||
case OpenThermConnectionStatus::OTC_CONNECTING:
|
||||
return "CONNECTING";
|
||||
case OpenThermConnectionStatus::OTC_HANDSHAKE:
|
||||
return "HANDSHAKE";
|
||||
case OpenThermConnectionStatus::OTC_READY:
|
||||
return "READY";
|
||||
case OpenThermConnectionStatus::OTC_INFLIGHT:
|
||||
return "BUSY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void sns_opentherm_init_boiler_status()
|
||||
{
|
||||
memset(&sns_ot_boiler_status, 0, sizeof(OT_BOILER_STATUS));
|
||||
|
||||
// Settings
|
||||
sns_ot_boiler_status.m_useDiagnosticIndicationAsHeatRequest = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics;
|
||||
sns_ot_boiler_status.m_enableHotWater = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableHotWater;
|
||||
sns_ot_boiler_status.m_enableCentralHeating = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating;
|
||||
sns_ot_boiler_status.m_enableCooling = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCooling;
|
||||
sns_ot_boiler_status.m_enableOutsideTemperatureCompensation = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableTemperatureCompensation;
|
||||
sns_ot_boiler_status.m_enableCentralHeating2 = Settings.ot_flags & (uint8_t)OpenThermSettingsFlags::EnableCentralHeating2;
|
||||
|
||||
sns_ot_boiler_status.m_boilerSetpoint = (float)Settings.ot_boiler_setpoint;
|
||||
sns_ot_boiler_status.m_hotWaterSetpoint = (float)Settings.ot_hot_water_setpoint;
|
||||
|
||||
sns_ot_boiler_status.m_fault_code = 0;
|
||||
sns_ot_boiler_status.m_oem_fault_code = 0;
|
||||
sns_ot_boiler_status.m_oem_diag_code = 0;
|
||||
sns_ot_boiler_status.m_hotWaterSetpoint_read = 0;
|
||||
sns_ot_boiler_status.m_flame_modulation_read = 0;
|
||||
sns_ot_boiler_status.m_boiler_temperature_read = 0;
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR sns_opentherm_handleInterrupt()
|
||||
{
|
||||
sns_ot_master->handleInterrupt();
|
||||
}
|
||||
|
||||
void sns_opentherm_processResponseCallback(unsigned long response, int st)
|
||||
{
|
||||
OpenThermResponseStatus status = (OpenThermResponseStatus)st;
|
||||
AddLog_P2(LOG_LEVEL_DEBUG_MORE,
|
||||
PSTR("[OTH]: Processing response. Status=%s, Response=0x%lX"),
|
||||
sns_ot_master->statusToString(status), response);
|
||||
|
||||
if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_HANDSHAKE)
|
||||
{
|
||||
return sns_ot_process_handshake(response, st);
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case OpenThermResponseStatus::SUCCESS:
|
||||
if (sns_ot_master->isValidResponse(response))
|
||||
{
|
||||
sns_opentherm_process_success_response(&sns_ot_boiler_status, response);
|
||||
}
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY;
|
||||
break;
|
||||
|
||||
case OpenThermResponseStatus::INVALID:
|
||||
sns_opentherm_check_retry_request();
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY;
|
||||
break;
|
||||
|
||||
// Timeout may indicate not valid/supported command or connection error
|
||||
// In this case we do reconnect.
|
||||
// If this command will timeout multiple times, it will be excluded from the rotation later on
|
||||
// after couple of failed attempts. See sns_opentherm_check_retry_request logic
|
||||
case OpenThermResponseStatus::TIMEOUT:
|
||||
sns_opentherm_check_retry_request();
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool sns_opentherm_Init()
|
||||
{
|
||||
if (PinUsed(GPIO_BOILER_OT_RX) && PinUsed(GPIO_BOILER_OT_TX))
|
||||
{
|
||||
sns_ot_master = new OpenTherm(Pin(GPIO_BOILER_OT_RX), Pin(GPIO_BOILER_OT_TX));
|
||||
sns_ot_master->begin(sns_opentherm_handleInterrupt, sns_opentherm_processResponseCallback);
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING;
|
||||
|
||||
sns_opentherm_init_boiler_status();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// !warning, sns_opentherm settings are not ready at this point
|
||||
}
|
||||
|
||||
void sns_opentherm_stat(bool json)
|
||||
{
|
||||
if (!sns_ot_master)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const char *statusStr = sns_opentherm_connection_stat_to_str(sns_ot_connection_status);
|
||||
|
||||
if (json)
|
||||
{
|
||||
ResponseAppend_P(PSTR(",\"OPENTHERM\":{"));
|
||||
ResponseAppend_P(PSTR("\"conn\":\"%s\","), statusStr);
|
||||
ResponseAppend_P(PSTR("\"settings\":%d,"), Settings.ot_flags);
|
||||
sns_opentherm_dump_telemetry();
|
||||
ResponseJsonEnd();
|
||||
#ifdef USE_WEBSERVER
|
||||
}
|
||||
else
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}OpenTherm status{m}%s (0x%X){e}"), statusStr, (int)sns_ot_boiler_status.m_slave_flags);
|
||||
if (sns_ot_connection_status < OpenThermConnectionStatus::OTC_READY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
WSContentSend_P(PSTR("{s}Std/OEM Fault Codes{m}%d / %d{e}"),
|
||||
(int)sns_ot_boiler_status.m_fault_code,
|
||||
(int)sns_ot_boiler_status.m_oem_fault_code);
|
||||
|
||||
WSContentSend_P(PSTR("{s}OEM Diagnostic Code{m}%d{e}"),
|
||||
(int)sns_ot_boiler_status.m_oem_diag_code);
|
||||
|
||||
WSContentSend_P(PSTR("{s}Hot Water Setpoint{m}%d{e}"),
|
||||
(int)sns_ot_boiler_status.m_hotWaterSetpoint_read);
|
||||
|
||||
WSContentSend_P(PSTR("{s}Flame Modulation{m}%d{e}"),
|
||||
(int)sns_ot_boiler_status.m_flame_modulation_read);
|
||||
|
||||
WSContentSend_P(PSTR("{s}Boiler Temp/Setpnt{m}%d / %d{e}"),
|
||||
(int)sns_ot_boiler_status.m_boiler_temperature_read,
|
||||
(int)sns_ot_boiler_status.m_boilerSetpoint);
|
||||
|
||||
if (OpenTherm::isCentralHeatingActive(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Central Heating is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (sns_ot_boiler_status.m_enableHotWater)
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Hot Water is Enabled{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isHotWaterActive(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Hot Water is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isFlameOn(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Flame is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (sns_ot_boiler_status.m_enableCooling)
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Cooling is Enabled{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isCoolingActive(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Cooling is ACTIVE{m}{e}"));
|
||||
}
|
||||
|
||||
if (OpenTherm::isDiagnostic(sns_ot_boiler_status.m_slave_raw_status))
|
||||
{
|
||||
WSContentSend_P(PSTR("{s}Diagnostic Indication{m}{e}"));
|
||||
}
|
||||
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
void sns_ot_start_handshake()
|
||||
{
|
||||
if (!sns_ot_master)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: perform handshake"));
|
||||
|
||||
sns_ot_master->sendRequestAync(
|
||||
OpenTherm::buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0));
|
||||
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_HANDSHAKE;
|
||||
}
|
||||
|
||||
void sns_ot_process_handshake(unsigned long response, int st)
|
||||
{
|
||||
OpenThermResponseStatus status = (OpenThermResponseStatus)st;
|
||||
|
||||
if (status != OpenThermResponseStatus::SUCCESS || !sns_ot_master->isValidResponse(response))
|
||||
{
|
||||
AddLog_P2(LOG_LEVEL_ERROR,
|
||||
PSTR("[OTH]: getSlaveConfiguration failed. Status=%s"),
|
||||
sns_ot_master->statusToString(status));
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("[OTH]: getLastResponseStatus SUCCESS. Slave Cfg: %lX"), response);
|
||||
|
||||
sns_ot_boiler_status.m_slave_flags = (response & 0xFF00) >> 8;
|
||||
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_READY;
|
||||
}
|
||||
|
||||
void sns_opentherm_CheckSettings(void)
|
||||
{
|
||||
bool settingsValid = true;
|
||||
|
||||
settingsValid &= Settings.ot_hot_water_setpoint >= OT_HOT_WATER_MIN;
|
||||
settingsValid &= Settings.ot_hot_water_setpoint <= OT_HOT_WATER_MAX;
|
||||
settingsValid &= Settings.ot_boiler_setpoint >= OT_BOILER_MIN;
|
||||
settingsValid &= Settings.ot_boiler_setpoint <= OT_BOILER_MAX;
|
||||
|
||||
if (!settingsValid)
|
||||
{
|
||||
Settings.ot_hot_water_setpoint = OT_HOT_WATER_DEFAULT;
|
||||
Settings.ot_boiler_setpoint = OT_BOILER_DEFAULT;
|
||||
Settings.ot_flags =
|
||||
OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics |
|
||||
OpenThermSettingsFlags::EnableHotWater;
|
||||
}
|
||||
}
|
||||
/*********************************************************************************************\
|
||||
* Command Processing
|
||||
\*********************************************************************************************/
|
||||
const char *sns_opentherm_flag_text(uint8_t mode)
|
||||
{
|
||||
switch ((OpenThermSettingsFlags)mode)
|
||||
{
|
||||
case OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics:
|
||||
return "CHOD";
|
||||
case OpenThermSettingsFlags::EnableHotWater:
|
||||
return "DHW";
|
||||
case OpenThermSettingsFlags::EnableCentralHeating:
|
||||
return "CH";
|
||||
case OpenThermSettingsFlags::EnableCooling:
|
||||
return "COOL";
|
||||
case OpenThermSettingsFlags::EnableTemperatureCompensation:
|
||||
return "OTC";
|
||||
case OpenThermSettingsFlags::EnableCentralHeating2:
|
||||
return "CH2";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t sns_opentherm_parse_flag(char *flag)
|
||||
{
|
||||
if (!strncmp(flag, "CHOD", 4))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCentralHeatingOnDiagnostics;
|
||||
}
|
||||
else if (!strncmp(flag, "COOL", 4))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCooling;
|
||||
}
|
||||
else if (!strncmp(flag, "DHW", 3))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableHotWater;
|
||||
}
|
||||
else if (!strncmp(flag, "OTC", 3))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableTemperatureCompensation;
|
||||
}
|
||||
else if (!strncmp(flag, "CH2", 3))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCentralHeating2;
|
||||
}
|
||||
else if (!strncmp(flag, "CH", 2))
|
||||
{
|
||||
return OpenThermSettingsFlags::EnableCentralHeating;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t sns_opentherm_read_flags(char *data, uint32_t len)
|
||||
{
|
||||
uint8_t tokens = 1;
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
if (data[i] == ',')
|
||||
{
|
||||
++tokens;
|
||||
}
|
||||
}
|
||||
uint8_t result = 0;
|
||||
char sub_string[XdrvMailbox.data_len + 1];
|
||||
for (int i = 1; i <= tokens; ++i)
|
||||
{
|
||||
char *flag = subStr(sub_string, data, ",", i);
|
||||
if (!flag)
|
||||
{
|
||||
break;
|
||||
}
|
||||
result |= sns_opentherm_parse_flag(flag);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#define D_PRFX_OTHERM "ot_"
|
||||
// set the boiler temperature (CH). Sutable for the PID app.
|
||||
// After restart will use the defaults from the settings
|
||||
#define D_CMND_OTHERM_BOILER_SETPOINT "tboiler"
|
||||
// set hot water (DHW) temperature. Do not write it in the flash memory.
|
||||
// suitable for the temporary changes
|
||||
#define D_CMND_OTHERM_DHW_SETPOINT "twater"
|
||||
// This command will save CH and DHW setpoints into the settings. Those values will be used after system restart
|
||||
// The reason to separate set and save is to reduce flash memory write count, especially if boiler temperature is controlled
|
||||
// by the PID thermostat
|
||||
#define D_CMND_OTHERM_SAVE_SETTINGS "save_setpoints"
|
||||
// Get or set flags
|
||||
|
||||
// EnableCentralHeatingOnDiagnostics -> CHOD
|
||||
// EnableHotWater -> DHW
|
||||
// EnableCentralHeating -> CH
|
||||
// EnableCooling -> COOL
|
||||
// EnableTemperatureCompensation -> OTC
|
||||
// EnableCentralHeating2 -> CH2
|
||||
#define D_CMND_OTHERM_FLAGS "flags"
|
||||
|
||||
// Get/Set boiler status m_enableCentralHeating value. It's equivalent of the EnableCentralHeating settings
|
||||
// flag value, however, this command does not update the settings.
|
||||
// Usefull to buld automations
|
||||
// Please note, if you set it to "0" and EnableCentralHeatingOnDiagnostics is set
|
||||
// boiler will follow the Diagnostics bit and won't turn CH off. When Diagnostics bit cleared,
|
||||
// and "ot_ch" is "1", boiler will keep heating
|
||||
#define D_CMND_SET_CENTRAL_HEATING_ENABLED "ch"
|
||||
|
||||
const char kOpenThermCommands[] PROGMEM = D_PRFX_OTHERM "|" D_CMND_OTHERM_BOILER_SETPOINT "|" D_CMND_OTHERM_DHW_SETPOINT
|
||||
"|" D_CMND_OTHERM_SAVE_SETTINGS "|" D_CMND_OTHERM_FLAGS "|" D_CMND_SET_CENTRAL_HEATING_ENABLED;
|
||||
|
||||
void (*const OpenThermCommands[])(void) PROGMEM = {
|
||||
&sns_opentherm_boiler_setpoint_cmd,
|
||||
&sns_opentherm_hot_water_setpoint_cmd,
|
||||
&sns_opentherm_save_settings_cmd,
|
||||
&sns_opentherm_flags_cmd,
|
||||
&sns_opentherm_set_central_heating_cmd};
|
||||
|
||||
void sns_opentherm_cmd(void) { }
|
||||
void sns_opentherm_boiler_setpoint_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
sns_ot_boiler_status.m_boilerSetpoint = atof(XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndFloat(sns_ot_boiler_status.m_boilerSetpoint, Settings.flag2.temperature_resolution);
|
||||
}
|
||||
|
||||
void sns_opentherm_hot_water_setpoint_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
sns_ot_boiler_status.m_hotWaterSetpoint = atof(XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndFloat(sns_ot_boiler_status.m_hotWaterSetpoint, Settings.flag2.temperature_resolution);
|
||||
}
|
||||
|
||||
void sns_opentherm_save_settings_cmd(void)
|
||||
{
|
||||
Settings.ot_hot_water_setpoint = (uint8_t)sns_ot_boiler_status.m_hotWaterSetpoint;
|
||||
Settings.ot_boiler_setpoint = (uint8_t)sns_ot_boiler_status.m_boilerSetpoint;
|
||||
ResponseCmndDone();
|
||||
}
|
||||
|
||||
void sns_opentherm_flags_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
// Set flags value
|
||||
Settings.ot_flags = sns_opentherm_read_flags(XdrvMailbox.data, XdrvMailbox.data_len);
|
||||
// Reset boiler status to apply settings
|
||||
sns_opentherm_init_boiler_status();
|
||||
}
|
||||
bool addComma = false;
|
||||
mqtt_data[0] = 0;
|
||||
for (int pos = 0; pos < OT_FLAGS_COUNT; ++pos)
|
||||
{
|
||||
int mask = 1 << pos;
|
||||
int mode = Settings.ot_flags & (uint8_t)mask;
|
||||
if (mode > 0)
|
||||
{
|
||||
if (addComma)
|
||||
{
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data);
|
||||
}
|
||||
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s"), mqtt_data, sns_opentherm_flag_text(mode));
|
||||
addComma = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sns_opentherm_set_central_heating_cmd(void)
|
||||
{
|
||||
bool query = strlen(XdrvMailbox.data) == 0;
|
||||
if (!query)
|
||||
{
|
||||
sns_ot_boiler_status.m_enableCentralHeating = atoi(XdrvMailbox.data);
|
||||
}
|
||||
ResponseCmndNumber(sns_ot_boiler_status.m_enableCentralHeating ? 1 : 0);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns68(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
if (FUNC_INIT == function)
|
||||
{
|
||||
if (sns_opentherm_Init())
|
||||
{
|
||||
sns_opentherm_CheckSettings();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sns_ot_master)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case FUNC_LOOP:
|
||||
sns_ot_master->process();
|
||||
break;
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_READY && sns_ot_master->isReady())
|
||||
{
|
||||
unsigned long request = sns_opentherm_get_next_request(&sns_ot_boiler_status);
|
||||
if (-1 != request)
|
||||
{
|
||||
sns_ot_master->sendRequestAync(request);
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_INFLIGHT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_DISCONNECTED)
|
||||
{
|
||||
// If disconnected, wait for the SNS_OT_DISCONNECT_COOLDOWN_SECONDS before the handshake
|
||||
if (sns_ot_disconnect_cooldown == 0)
|
||||
{
|
||||
sns_ot_disconnect_cooldown = SNS_OT_DISCONNECT_COOLDOWN_SECONDS;
|
||||
}
|
||||
else if (--sns_ot_disconnect_cooldown == 0)
|
||||
{
|
||||
sns_ot_connection_status = OpenThermConnectionStatus::OTC_CONNECTING;
|
||||
}
|
||||
}
|
||||
else if (sns_ot_connection_status == OpenThermConnectionStatus::OTC_CONNECTING)
|
||||
{
|
||||
sns_ot_start_handshake();
|
||||
}
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kOpenThermCommands, OpenThermCommands);
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
sns_opentherm_stat(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
sns_opentherm_stat(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_OPENTHERM
|
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
xsns_68_opentherm_protocol.ino - OpenTherm protocol support for Tasmota
|
||||
|
||||
Copyright (C) 2020 Yuriy Sannikov
|
||||
|
||||
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, either 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 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 "OpenTherm.h"
|
||||
|
||||
#ifdef USE_OPENTHERM
|
||||
|
||||
// Temperature tolerance. If temperature setpoint difference is less than the value,
|
||||
// OT (1)(Control setpoint) command will be skipped
|
||||
#define OPENTHERM_BOILER_SETPOINT_TOLERANCE 1.0
|
||||
|
||||
typedef union {
|
||||
uint8_t m_flags;
|
||||
struct
|
||||
{
|
||||
uint8_t notSupported : 1; // If set, boiler does not support this command
|
||||
uint8_t supported : 1; // Set if at least one response were successfull
|
||||
uint8_t retryCount : 2; // Retry counter before notSupported flag being set
|
||||
};
|
||||
} OpenThermParamFlags;
|
||||
|
||||
typedef union {
|
||||
float m_float;
|
||||
uint8_t m_u8;
|
||||
uint16_t m_u16;
|
||||
unsigned long m_ul;
|
||||
bool m_bool;
|
||||
} ResponseStorage;
|
||||
|
||||
typedef struct OpenThermCommandT
|
||||
{
|
||||
const char *m_command_name;
|
||||
uint8_t m_command_code;
|
||||
OpenThermParamFlags m_flags;
|
||||
ResponseStorage m_results[2];
|
||||
unsigned long (*m_ot_make_request)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus);
|
||||
void (*m_ot_parse_response)(OpenThermCommandT *self, OT_BOILER_STATUS_T *boilerStatus, unsigned long response);
|
||||
void (*m_ot_appent_telemetry)(OpenThermCommandT *self);
|
||||
} OpenThermCommand;
|
||||
|
||||
OpenThermCommand sns_opentherm_commands[] = {
|
||||
{// Get/Set Slave Status Flags
|
||||
.m_command_name = "SLAVE",
|
||||
.m_command_code = 0,
|
||||
// OpenTherm ID(0) should never go into the notSupported state due to some connectivity issues
|
||||
// otherwice it may lose boiler control
|
||||
.m_flags = {.supported = 1},
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_set_slave_flags,
|
||||
.m_ot_parse_response = sns_opentherm_parse_slave_flags,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_slave_flags},
|
||||
{// Set boiler temperature
|
||||
.m_command_name = "BTMP",
|
||||
.m_command_code = 0,
|
||||
// OpenTherm ID(1) also should never go into the notSupported state due to some connectivity issues
|
||||
.m_flags = {.supported = 1},
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_set_boiler_temperature,
|
||||
.m_ot_parse_response = sns_opentherm_parse_set_boiler_temperature,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_boiler_temperature},
|
||||
{// Set Hot Water temperature
|
||||
.m_command_name = "HWTMP",
|
||||
.m_command_code = 0,
|
||||
// OpenTherm ID(56) may not be supported
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_set_boiler_dhw_temperature,
|
||||
.m_ot_parse_response = sns_opentherm_parse_boiler_dhw_temperature,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_boiler_dhw_temperature},
|
||||
{// Read Application-specific fault flags and OEM fault code
|
||||
.m_command_name = "ASFF",
|
||||
.m_command_code = 0,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_flags,
|
||||
.m_ot_parse_response = sns_opentherm_parse_flags,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_flags},
|
||||
{// Read An OEM-specific diagnostic/service code
|
||||
.m_command_name = "OEMD",
|
||||
.m_command_code = 0,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_oem_diag,
|
||||
.m_ot_parse_response = sns_opentherm_parse_oem_diag,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_oem_diag},
|
||||
{// Read Flame modulation
|
||||
.m_command_name = "FLM",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::RelModLevel,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_flame_modulation,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read Boiler Temperature
|
||||
.m_command_name = "TB",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Tboiler,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_boiler_temperature,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read DHW temperature
|
||||
.m_command_name = "TDHW",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Tdhw,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read Outside temperature
|
||||
.m_command_name = "TOUT",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Toutside,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read Return water temperature
|
||||
.m_command_name = "TRET",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::Tret,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read DHW setpoint
|
||||
.m_command_name = "DHWS",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::TdhwSet,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_dhw_setpoint,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
{// Read max CH water setpoint
|
||||
.m_command_name = "TMAX",
|
||||
.m_command_code = (uint8_t)OpenThermMessageID::MaxTSet,
|
||||
.m_flags = 0,
|
||||
.m_results = {{.m_u8 = 0}, {.m_u8 = 0}},
|
||||
.m_ot_make_request = sns_opentherm_get_generic_float,
|
||||
.m_ot_parse_response = sns_opentherm_parse_generic_float,
|
||||
.m_ot_appent_telemetry = sns_opentherm_tele_generic_float},
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////// Process Slave Status Flags & Control //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_set_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status)
|
||||
{
|
||||
bool centralHeatingIsOn = status->m_enableCentralHeating;
|
||||
|
||||
if (status->m_useDiagnosticIndicationAsHeatRequest) {
|
||||
centralHeatingIsOn |= OpenTherm::isDiagnostic(status->m_slave_raw_status);
|
||||
}
|
||||
|
||||
if (self->m_results[1].m_bool != centralHeatingIsOn) {
|
||||
AddLog_P2(LOG_LEVEL_INFO,
|
||||
PSTR("[OTH]: Central Heating transitioning from %s to %s"),
|
||||
self->m_results[1].m_bool ? "on" : "off",
|
||||
status->m_enableCentralHeating ? "on" : "off");
|
||||
}
|
||||
self->m_results[1].m_bool = centralHeatingIsOn;
|
||||
|
||||
unsigned int data = centralHeatingIsOn |
|
||||
(status->m_enableHotWater << 1) |
|
||||
(status->m_enableCooling << 2) |
|
||||
(status->m_enableOutsideTemperatureCompensation << 3) |
|
||||
(status->m_enableCentralHeating2 << 4);
|
||||
|
||||
data <<= 8;
|
||||
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Status, data);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_slave_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
boilerStatus->m_slave_raw_status = response;
|
||||
self->m_results[0].m_ul = response;
|
||||
}
|
||||
|
||||
#define OT_FLAG_TO_ON_OFF(status, flag) ((((status) & (flag)) != 0) ? 1 : 0)
|
||||
void sns_opentherm_tele_slave_flags(struct OpenThermCommandT *self)
|
||||
{
|
||||
unsigned long st = self->m_results[0].m_ul;
|
||||
ResponseAppend_P(PSTR("{\"FAULT\":%d,\"CH\":%d,\"DHW\":%d,\"FL\":%d,\"COOL\":%d,\"CH2\":%d,\"DIAG\":%d,\"RAW\":%lu}"),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x01),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x02),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x04),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x08),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x10),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x20),
|
||||
OT_FLAG_TO_ON_OFF(st, 0x40),
|
||||
st);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Set Boiler Temperature //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status)
|
||||
{
|
||||
// Assuming some boilers might write setpoint temperature into the Flash memory
|
||||
// Having PID controlled appliance may produce a lot of small fluctuations in the setpoint value
|
||||
// wearing out Boiler flash memory.
|
||||
float diff = abs(status->m_boilerSetpoint - self->m_results[0].m_float);
|
||||
// Ignore small changes in the boiler setpoint temperature
|
||||
if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_INFO,
|
||||
PSTR("[OTH]: Setting Boiler Temp. Old: %d, New: %d"),
|
||||
(int)self->m_results[0].m_float,
|
||||
(int)status->m_boilerSetpoint);
|
||||
self->m_results[0].m_float = status->m_boilerSetpoint;
|
||||
|
||||
unsigned int data = OpenTherm::temperatureToData(status->m_boilerSetpoint);
|
||||
return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TSet, data);
|
||||
}
|
||||
void sns_opentherm_parse_set_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[1].m_float = OpenTherm::getFloat(response);
|
||||
}
|
||||
void sns_opentherm_tele_boiler_temperature(struct OpenThermCommandT *self)
|
||||
{
|
||||
char requested[FLOATSZ];
|
||||
dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested);
|
||||
char actual[FLOATSZ];
|
||||
dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual);
|
||||
|
||||
// indicate fault if tepmerature demand and actual setpoint are greater then tolerance
|
||||
bool isFault = abs(self->m_results[1].m_float - self->m_results[0].m_float) > OPENTHERM_BOILER_SETPOINT_TOLERANCE;
|
||||
|
||||
ResponseAppend_P(PSTR("{\"FAULT\":%d,\"REQ\":%s,\"ACT\": %s}"),
|
||||
(int)isFault,
|
||||
requested,
|
||||
actual);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Set Domestic Hot Water Temperature //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_set_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *status)
|
||||
{
|
||||
// The same consideration as for the boiler temperature
|
||||
float diff = abs(status->m_hotWaterSetpoint - self->m_results[0].m_float);
|
||||
// Ignore small changes in the boiler setpoint temperature
|
||||
if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
AddLog_P2(LOG_LEVEL_INFO,
|
||||
PSTR("[OTH]: Setting Hot Water Temp. Old: %d, New: %d"),
|
||||
(int)self->m_results[0].m_float,
|
||||
(int)status->m_hotWaterSetpoint);
|
||||
|
||||
self->m_results[0].m_float = status->m_hotWaterSetpoint;
|
||||
|
||||
unsigned int data = OpenTherm::temperatureToData(status->m_hotWaterSetpoint);
|
||||
return OpenTherm::buildRequest(OpenThermMessageType::WRITE_DATA, OpenThermMessageID::TdhwSet, data);
|
||||
}
|
||||
void sns_opentherm_parse_boiler_dhw_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[1].m_float = OpenTherm::getFloat(response);
|
||||
}
|
||||
void sns_opentherm_tele_boiler_dhw_temperature(struct OpenThermCommandT *self)
|
||||
{
|
||||
char requested[FLOATSZ];
|
||||
dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, requested);
|
||||
char actual[FLOATSZ];
|
||||
dtostrfd(self->m_results[1].m_float, Settings.flag2.temperature_resolution, actual);
|
||||
|
||||
ResponseAppend_P(PSTR("{\"REQ\":%s,\"ACT\": %s}"),
|
||||
requested,
|
||||
actual);
|
||||
}
|
||||
|
||||
/////////////////////////////////// App Specific Fault Flags //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_get_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *)
|
||||
{
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_flags(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
uint8_t fault_code = (response >> 8) & 0xFF;
|
||||
uint8_t oem_fault_code = response & 0xFF;
|
||||
boilerStatus->m_fault_code = fault_code;
|
||||
boilerStatus->m_oem_fault_code = fault_code;
|
||||
self->m_results[0].m_u8 = fault_code;
|
||||
self->m_results[1].m_u8 = oem_fault_code;
|
||||
}
|
||||
|
||||
void sns_opentherm_tele_flags(struct OpenThermCommandT *self)
|
||||
{
|
||||
ResponseAppend_P(PSTR("{\"FC\":%d,\"OFC\":%d}"),
|
||||
(int)self->m_results[0].m_u8,
|
||||
(int)self->m_results[1].m_u8);
|
||||
}
|
||||
|
||||
/////////////////////////////////// OEM Diag Code //////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_get_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *)
|
||||
{
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, OpenThermMessageID::OEMDiagnosticCode, 0);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_oem_diag(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
uint16_t diag_code = (uint16_t)response & 0xFFFF;
|
||||
boilerStatus->m_oem_diag_code = diag_code;
|
||||
self->m_results[0].m_u16 = diag_code;
|
||||
}
|
||||
|
||||
void sns_opentherm_tele_oem_diag(struct OpenThermCommandT *self)
|
||||
{
|
||||
ResponseAppend_P(PSTR("%d"), (int)self->m_results[0].m_u16);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Generic Single Float /////////////////////////////////////////////////
|
||||
unsigned long sns_opentherm_get_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *)
|
||||
{
|
||||
return OpenTherm::buildRequest(OpenThermRequestType::READ, (OpenThermMessageID)self->m_command_code, 0);
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_generic_float(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
}
|
||||
|
||||
void sns_opentherm_tele_generic_float(struct OpenThermCommandT *self)
|
||||
{
|
||||
char str[FLOATSZ];
|
||||
dtostrfd(self->m_results[0].m_float, Settings.flag2.temperature_resolution, str);
|
||||
ResponseAppend_P(PSTR("%s"), str);
|
||||
}
|
||||
|
||||
/////////////////////////////////// Specific Floats Rerports to the /////////////////////////////////////////////////
|
||||
void sns_opentherm_parse_dhw_setpoint(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
boilerStatus->m_hotWaterSetpoint_read = self->m_results[0].m_float;
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_flame_modulation(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
boilerStatus->m_flame_modulation_read = self->m_results[0].m_float;
|
||||
}
|
||||
|
||||
void sns_opentherm_parse_boiler_temperature(struct OpenThermCommandT *self, struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
self->m_results[0].m_float = OpenTherm::getFloat(response);
|
||||
boilerStatus->m_boiler_temperature_read = self->m_results[0].m_float;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define SNS_OT_COMMANDS_COUNT (sizeof(sns_opentherm_commands) / sizeof(OpenThermCommand))
|
||||
int sns_opentherm_current_command = SNS_OT_COMMANDS_COUNT;
|
||||
|
||||
unsigned long sns_opentherm_get_next_request(struct OT_BOILER_STATUS_T *boilerStatus)
|
||||
{
|
||||
// get next and loop the command
|
||||
if (++sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT)
|
||||
{
|
||||
sns_opentherm_current_command = 0;
|
||||
}
|
||||
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command];
|
||||
// Return error if command known as not supported
|
||||
if (cmd->m_flags.notSupported)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
// Retrurn OT compatible request
|
||||
return cmd->m_ot_make_request(cmd, boilerStatus);
|
||||
}
|
||||
|
||||
void sns_opentherm_check_retry_request()
|
||||
{
|
||||
if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command];
|
||||
|
||||
bool canRetry = ++cmd->m_flags.retryCount < 3;
|
||||
// In case of last retry and if this command never respond successfully, set notSupported flag
|
||||
if (!canRetry && !cmd->m_flags.supported)
|
||||
{
|
||||
cmd->m_flags.notSupported = true;
|
||||
AddLog_P2(LOG_LEVEL_ERROR,
|
||||
PSTR("[OTH]: command %s is not supported by the boiler. Last status: %s"),
|
||||
cmd->m_command_name,
|
||||
sns_ot_master->statusToString(sns_ot_master->getLastResponseStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
void sns_opentherm_process_success_response(struct OT_BOILER_STATUS_T *boilerStatus, unsigned long response)
|
||||
{
|
||||
if (sns_opentherm_current_command >= SNS_OT_COMMANDS_COUNT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[sns_opentherm_current_command];
|
||||
// mark command as supported
|
||||
cmd->m_flags.supported = true;
|
||||
|
||||
cmd->m_ot_parse_response(cmd, boilerStatus, response);
|
||||
}
|
||||
|
||||
void sns_opentherm_dump_telemetry()
|
||||
{
|
||||
bool add_coma = false;
|
||||
for (int i = 0; i < SNS_OT_COMMANDS_COUNT; ++i)
|
||||
{
|
||||
struct OpenThermCommandT *cmd = &sns_opentherm_commands[i];
|
||||
if (!cmd->m_flags.supported)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ResponseAppend_P(PSTR("%s\"%s\":"), add_coma ? "," : "", cmd->m_command_name);
|
||||
|
||||
cmd->m_ot_appent_telemetry(cmd);
|
||||
|
||||
add_coma = true;
|
||||
}
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue