mirror of
https://github.com/anklimov/lighthub
synced 2025-12-12 06:39:51 +03:00
Merge pull request #52 from anklimov/GenericOut
Core refactored - 'drivers' for items added MQTT topics structure changed driver example for SPI LED driver example for Air conditioner HAIER Termostat compatibility for HomeKit and HomeAssistant
This commit is contained in:
41
lighthub/abstractch.cpp
Normal file
41
lighthub/abstractch.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
#include "abstractch.h"
|
||||
#include <PubSubClient.h>
|
||||
#include "utils.h"
|
||||
#include <aJSON.h>
|
||||
#include "main.h"
|
||||
|
||||
extern lan_status lanStatus;
|
||||
extern PubSubClient mqttClient;
|
||||
|
||||
int abstractCh::publishTopic(const char* topic, long value, const char* subtopic)
|
||||
{
|
||||
char valstr[16];
|
||||
printUlongValueToStr(valstr, value);
|
||||
return publishTopic(topic, valstr,subtopic);
|
||||
};
|
||||
|
||||
int abstractCh::publishTopic(const char* topic, float value, const char* subtopic)
|
||||
{
|
||||
char valstr[16];
|
||||
printFloatValueToStr(value, valstr);
|
||||
return publishTopic(topic, valstr,subtopic);
|
||||
};
|
||||
|
||||
int abstractCh::publishTopic(const char* topic, const char * value, const char* subtopic)
|
||||
{
|
||||
char addrstr[MQTT_TOPIC_LENGTH];
|
||||
|
||||
if (topic)
|
||||
{
|
||||
strncpy(addrstr,topic,sizeof(addrstr));
|
||||
if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,topic);
|
||||
strncat(addrstr,subtopic,sizeof(addrstr));
|
||||
if (mqttClient.connected() && lanStatus == OPERATION)
|
||||
{
|
||||
mqttClient.publish(addrstr, value, true);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
23
lighthub/abstractch.h
Normal file
23
lighthub/abstractch.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "Arduino.h"
|
||||
|
||||
#define CST_UNKNOWN 0
|
||||
#define CST_INITIALIZED 1
|
||||
|
||||
class abstractCh {
|
||||
public:
|
||||
abstractCh(){};
|
||||
virtual ~abstractCh(){};
|
||||
virtual int Poll() = 0;
|
||||
virtual int Setup() =0; //Should initialize hardware and reserve resources
|
||||
virtual int Anounce () {return 0;};
|
||||
virtual int Stop() {return 0;}; //Should free resources
|
||||
virtual int Status() {return CST_UNKNOWN;}
|
||||
|
||||
|
||||
protected:
|
||||
virtual int publishTopic(const char* topic, long value, const char* subtopic = NULL);
|
||||
virtual int publishTopic(const char* topic, float value, const char* subtopic = NULL );
|
||||
virtual int publishTopic(const char* topic, const char * value, const char* subtopic = NULL);
|
||||
//friend Input;
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
#include "abstractin.h"
|
||||
#include "abstractch.h"
|
||||
#include <PubSubClient.h>
|
||||
#include "utils.h"
|
||||
#include <aJSON.h>
|
||||
@@ -9,21 +10,21 @@
|
||||
extern lan_status lanStatus;
|
||||
extern PubSubClient mqttClient;
|
||||
|
||||
int abstractIn::publish(long value, char* subtopic)
|
||||
int abstractIn::publish(long value, const char* subtopic)
|
||||
{
|
||||
char valstr[16];
|
||||
printUlongValueToStr(valstr, value);
|
||||
return publish(valstr,subtopic);
|
||||
};
|
||||
|
||||
int abstractIn::publish(float value, char* subtopic)
|
||||
int abstractIn::publish(float value, const char* subtopic)
|
||||
{
|
||||
char valstr[16];
|
||||
printFloatValueToStr(value, valstr);
|
||||
return publish(valstr,subtopic);
|
||||
};
|
||||
|
||||
int abstractIn::publish(char * value, char* subtopic)
|
||||
int abstractIn::publish(char * value, const char* subtopic)
|
||||
{
|
||||
char addrstr[MQTT_TOPIC_LENGTH];
|
||||
if (in)
|
||||
@@ -31,14 +32,7 @@ int abstractIn::publish(char * value, char* subtopic)
|
||||
aJsonObject *emit = aJson.getObjectItem(in->inputObj, "emit");
|
||||
if (emit)
|
||||
{
|
||||
strncpy(addrstr,emit->valuestring,sizeof(addrstr));
|
||||
if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestring);
|
||||
strncat(addrstr,subtopic,sizeof(addrstr));
|
||||
if (mqttClient.connected() && lanStatus == OPERATION)
|
||||
{
|
||||
mqttClient.publish(addrstr, value, true);
|
||||
return 1;
|
||||
}
|
||||
return publishTopic(emit->valuestring,value,subtopic);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
#pragma once
|
||||
#include "Arduino.h"
|
||||
#include "abstractch.h"
|
||||
|
||||
class Input;
|
||||
class abstractIn {
|
||||
class abstractIn : public abstractCh{
|
||||
public:
|
||||
abstractIn(Input * _in){in=_in;};
|
||||
virtual int Setup(int addr) = 0;
|
||||
virtual int Poll() = 0;
|
||||
abstractIn(Input * _in):abstractCh(){in=_in;};
|
||||
|
||||
protected:
|
||||
Input * in;
|
||||
int publish(long value, char* subtopic = NULL);
|
||||
int publish(float value, char* subtopic = NULL );
|
||||
int publish(char * value, char* subtopic = NULL);
|
||||
int publish(long value, const char* subtopic = NULL);
|
||||
int publish(float value, const char* subtopic = NULL );
|
||||
int publish(char * value, const char* subtopic = NULL);
|
||||
friend Input;
|
||||
};
|
||||
|
||||
14
lighthub/abstractout.h
Normal file
14
lighthub/abstractout.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "Arduino.h"
|
||||
#include "abstractch.h"
|
||||
|
||||
class Item;
|
||||
class abstractOut : public abstractCh{
|
||||
public:
|
||||
abstractOut(Item * _item):abstractCh(){item=_item;};
|
||||
virtual int Ctrl(short cmd, short n=0, int * Parameters=NULL, boolean send=true, int suffixCode=0, char* subItem=NULL) =0;
|
||||
virtual int isActive(){return 0;};
|
||||
virtual int getDefaultOnVal(){return 100;};
|
||||
protected:
|
||||
Item * item;
|
||||
};
|
||||
@@ -118,12 +118,12 @@ if (!isValid() || (!root)) return;
|
||||
if (inType == IN_CCS811)
|
||||
{
|
||||
in_ccs811 ccs811(this);
|
||||
ccs811.Setup(pin);
|
||||
ccs811.Setup();
|
||||
}
|
||||
else if (inType == IN_HDC1080)
|
||||
{
|
||||
in_hdc1080 hdc1080(this);
|
||||
hdc1080.Setup(pin);
|
||||
hdc1080.Setup();
|
||||
}
|
||||
// TODO rest types setup
|
||||
#endif
|
||||
@@ -188,6 +188,7 @@ switch (cause) {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef COUNTER_DISABLE
|
||||
@@ -226,7 +227,7 @@ void Input::counterPoll() {
|
||||
char addrstr[MQTT_TOPIC_LENGTH];
|
||||
strncpy(addrstr,emit->valuestring,sizeof(addrstr));
|
||||
if (!strchr(addrstr,'/')) setTopic(addrstr,sizeof(addrstr),T_OUT,emit->valuestring);
|
||||
sprintf(valstr, "%d", counterValue);
|
||||
sprintf(valstr, "%ld", counterValue);
|
||||
if (mqttClient.connected() && !ethernetIdleCount)
|
||||
mqttClient.publish(addrstr, valstr);
|
||||
setNextPollTime(millis() + DHT_POLL_DELAY_DEFAULT);
|
||||
@@ -435,13 +436,15 @@ void Input::contactPoll() {
|
||||
if (inType & IN_PUSH_TOGGLE) {
|
||||
if (currentInputState) { //react on leading edge only (change from 0 to 1)
|
||||
store->logicState = !store->logicState;
|
||||
store->currentValue = currentInputState;
|
||||
onContactChanged(store->logicState);
|
||||
}
|
||||
} else {
|
||||
store->logicState = currentInputState;
|
||||
store->currentValue = currentInputState;
|
||||
onContactChanged(currentInputState);
|
||||
}
|
||||
store->currentValue = currentInputState;
|
||||
// store->currentValue = currentInputState;
|
||||
}
|
||||
} else // no change
|
||||
store->bounce = SAME_STATE_ATTEMPTS;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
142
lighthub/item.h
142
lighthub/item.h
@@ -18,18 +18,26 @@ e-mail anklimov@gmail.com
|
||||
|
||||
*/
|
||||
#include "options.h"
|
||||
#include "abstractout.h"
|
||||
|
||||
#define S_SET 1
|
||||
#define S_TEMP 2
|
||||
#define S_MODE 3
|
||||
#define S_SETPOINT 4
|
||||
#define S_POWER 5
|
||||
#define S_VOL 6
|
||||
#define S_HEAT 7
|
||||
#define S_HSV 8
|
||||
#define S_RGB 9
|
||||
#define S_RPM 10
|
||||
#define S_NOTFOUND 0
|
||||
#define S_SETnCMD 0
|
||||
#define S_CMD 1
|
||||
#define S_SET 2
|
||||
#define S_HSV 3
|
||||
#define S_RGB 4
|
||||
#define S_FAN 5
|
||||
#define S_MODE 6
|
||||
#define S_ADDITIONAL 64
|
||||
/*
|
||||
|
||||
#define S_RPM 11
|
||||
#define S_TEMP 3
|
||||
#define S_SETPOINT 5
|
||||
#define S_POWER 6
|
||||
#define S_VOL 7
|
||||
#define S_HEAT 8
|
||||
*/
|
||||
#define CH_DIMMER 0 //DMX 1 ch
|
||||
#define CH_RGBW 1 //DMX 4 ch
|
||||
#define CH_RGB 2 //DMX 3 ch
|
||||
@@ -40,7 +48,8 @@ e-mail anklimov@gmail.com
|
||||
#define CH_GROUP 7 //Group pseudochannel
|
||||
#define CH_VCTEMP 8 //Vacom PID regulator
|
||||
#define CH_VC 9 //Vacom modbus motor regulator
|
||||
#define CH_AC_HAIER 10 //AC Haier
|
||||
#define CH_AC 10 //AC Haier
|
||||
#define CH_SPILED 11
|
||||
#define CH_WHITE 127//
|
||||
|
||||
#define CMD_NUM 0
|
||||
@@ -58,12 +67,24 @@ e-mail anklimov@gmail.com
|
||||
#define CMD_XOFF 7 //off only if was previously turned on by CMD_XON
|
||||
#define CMD_UP 8 //increase
|
||||
#define CMD_DN 9 //decrease
|
||||
#define CMD_SET 0xe
|
||||
#define CMD_HEAT 0xa
|
||||
#define CMD_COOL 0xb
|
||||
#define CMD_AUTO 0xc
|
||||
#define CMD_FAN 0xd
|
||||
#define CMD_DRY 0xe
|
||||
#define CMD_SET 0xf
|
||||
#define CMD_CURTEMP 0xf
|
||||
#define CMD_MASK 0xf
|
||||
#define FLAG_MASK 0xf0
|
||||
|
||||
#define CMD_CURTEMP 127
|
||||
#define CMD_RETRY 64
|
||||
#define CMD_REPORT 32
|
||||
|
||||
#define SEND_COMMAND 16
|
||||
#define SEND_PARAMETERS 32
|
||||
#define SEND_RETRY 64
|
||||
#define SEND_DEFFERED 128
|
||||
|
||||
|
||||
//#define CMD_REPORT 32
|
||||
|
||||
#define I_TYPE 0 //Type of item
|
||||
#define I_ARG 1 //Chanel-type depended argument or array of arguments (pin, address etc)
|
||||
@@ -89,49 +110,61 @@ extern short thermoSetCurTemp(char *name, float t);
|
||||
|
||||
int txt2cmd (char * payload);
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef union
|
||||
{
|
||||
long int aslong;
|
||||
float asfloat;
|
||||
struct
|
||||
{ uint8_t v;
|
||||
uint8_t s;
|
||||
uint16_t h:15;
|
||||
uint16_t hsv_flag:1;
|
||||
};
|
||||
struct
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t w:7;
|
||||
uint8_t rgb_flag:1;
|
||||
};
|
||||
} CHstore;
|
||||
|
||||
/*
|
||||
typedef union
|
||||
{
|
||||
long int aslong;
|
||||
struct
|
||||
{
|
||||
int16_t h;
|
||||
int8_t s;
|
||||
int8_t v;
|
||||
};
|
||||
} HSVstore;
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
long int aslong;
|
||||
struct
|
||||
{
|
||||
int8_t r;
|
||||
int8_t g;
|
||||
int8_t b;
|
||||
int8_t v;
|
||||
};
|
||||
} RGBVstore;
|
||||
} RGBWstore;
|
||||
*/
|
||||
#pragma pack(pop)
|
||||
|
||||
class Item
|
||||
{
|
||||
public:
|
||||
aJsonObject *itemArr, *itemArg,*itemVal;
|
||||
uint8_t itemType;
|
||||
|
||||
abstractOut * driver;
|
||||
|
||||
Item(char * name);
|
||||
Item(aJsonObject * obj);
|
||||
~Item();
|
||||
|
||||
boolean isValid ();
|
||||
virtual int Ctrl(short cmd, short n=0, int * Parameters=NULL, boolean send=true, int subItem=0);
|
||||
boolean Setup();
|
||||
virtual int Ctrl(short cmd, short n=0, int * Parameters=NULL, boolean send=true, int suffixCode=0, char* subItem=NULL);
|
||||
virtual int Ctrl(char * payload, boolean send=true, char * subItem=NULL);
|
||||
|
||||
int getArg(short n=0);
|
||||
boolean getEnableCMD(int delta);
|
||||
//int getVal(short n); //From VAL array. Negative if no array
|
||||
long int getVal(); //From int val OR array
|
||||
uint8_t getCmd(bool ext = false);
|
||||
uint8_t getCmd();
|
||||
void setCmd(uint8_t cmdValue);
|
||||
short getFlag (short flag=FLAG_MASK);
|
||||
void setFlag (short flag);
|
||||
void clearFlag (short flag);
|
||||
//void setVal(uint8_t n, int par);
|
||||
void setVal(long int par);
|
||||
//void copyPar (aJsonObject *itemV);
|
||||
@@ -139,13 +172,15 @@ class Item
|
||||
inline int Off(){return Ctrl(CMD_OFF);};
|
||||
inline int Toggle(){return Ctrl(CMD_TOGGLE);};
|
||||
int Poll();
|
||||
int SendStatus(short cmd, short n=0, int * Par=NULL, boolean deferred = false);
|
||||
int SendStatus(int sendFlags);
|
||||
|
||||
protected:
|
||||
short cmd2changeActivity(int lastActivity, short defaultCmd = CMD_SET);
|
||||
int VacomSetFan (int8_t val, int8_t cmd=0);
|
||||
int VacomSetHeat(int addr, int8_t val, int8_t cmd=0);
|
||||
int modbusDimmerSet(int addr, uint16_t _reg, int _regType, int _mask, uint16_t value);
|
||||
void mb_fail(short addr, short op, int val, int cmd);
|
||||
int modbusDimmerSet(uint16_t value);
|
||||
void mb_fail();
|
||||
int isActive();
|
||||
void Parse();
|
||||
int checkModbusDimmer();
|
||||
@@ -156,32 +191,3 @@ class Item
|
||||
int checkFM();
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
||||
class PooledItem : public Item
|
||||
{
|
||||
public:
|
||||
virtual int onContactChanged() = 0;
|
||||
virtual void Idle ();
|
||||
protected:
|
||||
int PoolingInterval;
|
||||
unsigned long next;
|
||||
virtual int Pool() =0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Vacon : public Item
|
||||
{
|
||||
public:
|
||||
int Pool ();
|
||||
virtual int Ctrl(short cmd, short n=0, int * Par=NULL);
|
||||
protected:
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
@@ -265,8 +265,9 @@ else
|
||||
|
||||
Item item(itemName);
|
||||
if (item.isValid()) {
|
||||
/*
|
||||
if (item.itemType == CH_GROUP && (lanStatus == RETAINING_COLLECTING))
|
||||
return; //Do not restore group channels - they consist not relevant data
|
||||
return; //Do not restore group channels - they consist not relevant data */
|
||||
item.Ctrl((char *)payload, !(lanStatus == RETAINING_COLLECTING),subItem);
|
||||
} //valid item
|
||||
}
|
||||
@@ -317,7 +318,7 @@ lan_status lanLoop() {
|
||||
//Unsubscribe from status topics..
|
||||
//strncpy_P(buf, outprefix, sizeof(buf));
|
||||
setTopic(buf,sizeof(buf),T_OUT);
|
||||
strncat(buf, "#", sizeof(buf));
|
||||
strncat(buf, "+/+/#", sizeof(buf)); // Subscribing only on separated command/parameters topics
|
||||
mqttClient.unsubscribe(buf);
|
||||
|
||||
lanStatus = OPERATION;//3;
|
||||
@@ -582,7 +583,7 @@ void ip_ready_config_loaded_connecting_to_broker() {
|
||||
|
||||
// strncpy_P(buf, outprefix, sizeof(buf));
|
||||
setTopic(buf,sizeof(buf),T_OUT);
|
||||
strncat(buf, "#", sizeof(buf));
|
||||
strncat(buf, "+/+/#", sizeof(buf)); // Only on separated cmd/val topics
|
||||
mqttClient.subscribe(buf);
|
||||
|
||||
//Subscribing for command topics
|
||||
@@ -907,7 +908,8 @@ void applyConfig() {
|
||||
while (items && item)
|
||||
if (item->type == aJson_Array && aJson.getArraySize(item)>1) {
|
||||
Item it(item);
|
||||
if (it.isValid()) {
|
||||
if (it.isValid() && !it.Setup()) {
|
||||
//Legacy Setup
|
||||
short inverse = 0;
|
||||
int pin=it.getArg();
|
||||
if (pin<0) {pin=-pin; inverse = 1;}
|
||||
@@ -1140,7 +1142,7 @@ void cmdFunctionPwd(int arg_cnt, char **args)
|
||||
}
|
||||
|
||||
void cmdFunctionSetMac(int arg_cnt, char **args) {
|
||||
if (sscanf(args[1], "%x:%x:%x:%x:%x:%x%с", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) < 6) {
|
||||
if (sscanf(args[1], "%x:%x:%x:%x:%x:%x%c", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) < 6) {
|
||||
debugSerial<<F("could not parse: ")<<args[1];
|
||||
return;
|
||||
}
|
||||
@@ -1162,12 +1164,12 @@ void saveFlash(short n, char *str) {
|
||||
if (len>MAXFLASHSTR-1) len=MAXFLASHSTR-1;
|
||||
for(int i=0;i<len;i++) EEPROM.write(n+i,str[i]);
|
||||
EEPROM.write(n+len,0);
|
||||
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
// write the data to EEPROM
|
||||
short res = EEPROM.commitReset();
|
||||
Serial.println((res) ? "EEPROM Commit OK" : "Commit failed");
|
||||
#endif.
|
||||
#endif
|
||||
}
|
||||
|
||||
int loadFlash(short n, char *str, short l) {
|
||||
@@ -1186,7 +1188,7 @@ void saveFlash(short n, IPAddress& ip) {
|
||||
// write the data to EEPROM
|
||||
short res = EEPROM.commitReset();
|
||||
Serial.println((res) ? "EEPROM Commit OK" : "Commit failed");
|
||||
#endif.
|
||||
#endif
|
||||
}
|
||||
|
||||
int ipLoadFromFlash(short n, IPAddress &ip) {
|
||||
@@ -1743,9 +1745,10 @@ void inputSetup(void) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MODBUS_DISABLE
|
||||
//#ifndef MODBUS_DISABLE
|
||||
void pollingLoop(void) {
|
||||
boolean done = false;
|
||||
if (lanStatus == RETAINING_COLLECTING) return;
|
||||
if (millis() > nextPollingCheck) {
|
||||
while (pollingItem && !done) {
|
||||
if (pollingItem->type == aJson_Array) {
|
||||
@@ -1761,7 +1764,7 @@ void pollingLoop(void) {
|
||||
} //while
|
||||
}//if
|
||||
}
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
bool isThermostatWithMinArraySize(aJsonObject *item, int minimalArraySize) {
|
||||
return (item->type == aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint == CH_THERMO) &&
|
||||
@@ -1769,8 +1772,15 @@ bool isThermostatWithMinArraySize(aJsonObject *item, int minimalArraySize) {
|
||||
}
|
||||
|
||||
bool thermoDisabledOrDisconnected(aJsonObject *thermoExtensionArray, int thermoStateCommand) {
|
||||
return thermoStateCommand == CMD_OFF || thermoStateCommand == CMD_HALT ||
|
||||
aJson.getArrayItem(thermoExtensionArray, IET_ATTEMPTS)->valueint == 0;
|
||||
if (aJson.getArrayItem(thermoExtensionArray, IET_ATTEMPTS)->valueint == 0) return true;
|
||||
switch (thermoStateCommand) {
|
||||
case CMD_ON:
|
||||
case CMD_XON:
|
||||
case CMD_AUTO:
|
||||
case CMD_HEAT:
|
||||
return false;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ static bool HDC1080ready = false;
|
||||
static bool CCS811ready = false;
|
||||
|
||||
|
||||
int in_ccs811::Setup(int addr)
|
||||
int in_ccs811::Setup()
|
||||
{
|
||||
if (CCS811ready) {debugSerial<<F("ccs811 is already initialized")<<endl; return 0;}
|
||||
|
||||
@@ -52,7 +52,7 @@ delay(2000); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int in_hdc1080::Setup(int addr)
|
||||
int in_hdc1080::Setup()
|
||||
{
|
||||
if (HDC1080ready) {debugSerial<<F("hdc1080 is already initialized")<<endl; return 0;}
|
||||
Serial.println("HDC1080 Init ");
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
//CCS811 ccs811(CCS811_ADDR);
|
||||
//uint16_t ccs811Baseline;
|
||||
in_ccs811(Input * _in):abstractIn(_in){};
|
||||
int Setup(int addr) override;
|
||||
int Setup() override;
|
||||
int Poll() override;
|
||||
|
||||
protected:
|
||||
@@ -52,7 +52,7 @@ class in_hdc1080 : public abstractIn {
|
||||
public:
|
||||
//ClosedCube_HDC1080 hdc1080;
|
||||
in_hdc1080(Input * _in):abstractIn(_in){};
|
||||
int Setup(int addr) override;
|
||||
int Setup() override;
|
||||
int Poll() override;
|
||||
|
||||
protected:
|
||||
|
||||
433
lighthub/modules/out_ac.cpp
Normal file
433
lighthub/modules/out_ac.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
#ifndef AC_DISABLE
|
||||
|
||||
#include "modules/out_ac.h"
|
||||
#include "Arduino.h"
|
||||
#include "options.h"
|
||||
#include "Streaming.h"
|
||||
#include "item.h"
|
||||
|
||||
#define AC_Serial Serial3
|
||||
#define INTERVAL_AC_POLLING 5000L
|
||||
|
||||
static int driverStatus = CST_UNKNOWN;
|
||||
|
||||
static int fresh =0;
|
||||
static int power = 0;
|
||||
static int swing =0;
|
||||
static int lock_rem =0;
|
||||
static int cur_tmp = 0;
|
||||
static int set_tmp = 0;
|
||||
static int fan_spd = 0;
|
||||
static int mode = 0;
|
||||
|
||||
long prevPolling = 0;
|
||||
byte inCheck = 0;
|
||||
byte qstn[] = {255,255,10,0,0,0,0,0,1,1,77,1,90}; // Команда опроса
|
||||
byte data[37] = {}; //Массив данных
|
||||
byte on[] = {255,255,10,0,0,0,0,0,1,1,77,2,91}; // Включение кондиционера
|
||||
byte off[] = {255,255,10,0,0,0,0,0,1,1,77,3,92}; // Выключение кондиционера
|
||||
// byte lock[] = {255,255,10,0,0,0,0,0,1,3,0,0,14}; // Блокировка пульта
|
||||
|
||||
//Extended subItem set
|
||||
const char LOCK_P[] PROGMEM = "lock";
|
||||
const char QUIET_P[] PROGMEM = "queit";
|
||||
const char SWING_P[] PROGMEM = "swing";
|
||||
const char RAW_P[] PROGMEM = "raw";
|
||||
extern char HEAT_P[] PROGMEM;
|
||||
extern char COOL_P[] PROGMEM;
|
||||
extern char AUTO_P[] PROGMEM;
|
||||
extern char FAN_ONLY_P[] PROGMEM;
|
||||
extern char DRY_P[] PROGMEM;
|
||||
|
||||
void out_AC::InsertData(byte data[], size_t size){
|
||||
|
||||
char s_mode[10];
|
||||
set_tmp = data[B_SET_TMP]+16;
|
||||
cur_tmp = data[B_CUR_TMP];
|
||||
mode = data[B_MODE];
|
||||
fan_spd = data[B_FAN_SPD];
|
||||
swing = data[B_SWING];
|
||||
power = data[B_POWER];
|
||||
lock_rem = data[B_LOCK_REM];
|
||||
fresh = data[B_FRESH];
|
||||
/////////////////////////////////
|
||||
if (fresh & 0x01)
|
||||
publishTopic(item->itemArr->name, "ON","/fresh");
|
||||
else publishTopic(item->itemArr->name, "OFF","/fresh");
|
||||
|
||||
/////////////////////////////////
|
||||
if (lock_rem == 0x80){
|
||||
publishTopic(item->itemArr->name, "ON","/lock");
|
||||
}
|
||||
if (lock_rem == 0x00){
|
||||
publishTopic(item->itemArr->name, "OFF","/lock");
|
||||
}
|
||||
/////////////////////////////////
|
||||
/*
|
||||
if (power == 0x01 || power == 0x11){
|
||||
publishTopic(item->itemArr->name,"Power", "on");
|
||||
}
|
||||
if (power == 0x00 || power == 0x10){
|
||||
publishTopic(item->itemArr->name,"Power", "off");
|
||||
}
|
||||
*/
|
||||
|
||||
Serial.print ("Power=");
|
||||
Serial.println(power);
|
||||
|
||||
if (power & 0x08)
|
||||
publishTopic(item->itemArr->name, "ON", "/quiet");
|
||||
else publishTopic(item->itemArr->name, "OFF" , "/quiet");
|
||||
|
||||
|
||||
if (power == 3 || power == 2)
|
||||
publishTopic(item->itemArr->name, "on","/compressor");
|
||||
else
|
||||
publishTopic(item->itemArr->name, "off","/compressor");
|
||||
|
||||
|
||||
publishTopic(item->itemArr->name, (long) swing,"/swing");
|
||||
publishTopic(item->itemArr->name, (long) fan_spd,"/fan");
|
||||
|
||||
/////////////////////////////////
|
||||
/*
|
||||
if (swing == 0x00){
|
||||
publishTopic(item->itemArr->name, "off","swing");
|
||||
}
|
||||
if (swing == 0x01){
|
||||
publishTopic(item->itemArr->name, "ud","swing");
|
||||
}
|
||||
if (swing == 0x02){
|
||||
publishTopic(item->itemArr->name, "lr","swing");
|
||||
}
|
||||
if (swing == 0x03){
|
||||
publishTopic(item->itemArr->name, "all","swing");
|
||||
}
|
||||
/////////////////////////////////
|
||||
if (fan_spd == 0x00){
|
||||
publishTopic(item->itemArr->name, "max","fan");
|
||||
}
|
||||
if (fan_spd == 0x01){
|
||||
publishTopic(item->itemArr->name, "mid","fan");
|
||||
}
|
||||
if (fan_spd == 0x02){
|
||||
publishTopic(item->itemArr->name, "min","fan");
|
||||
}
|
||||
if (fan_spd == 0x03){
|
||||
publishTopic(item->itemArr->name, "auto","fan");
|
||||
}
|
||||
*/
|
||||
/////////////////////////////////
|
||||
|
||||
publishTopic(item->itemArr->name,(long)set_tmp,"/set");
|
||||
publishTopic(item->itemArr->name, (long)cur_tmp, "/temp");
|
||||
////////////////////////////////////
|
||||
s_mode[0]='\0';
|
||||
|
||||
if (mode == 0x00){
|
||||
strcpy_P(s_mode,AUTO_P);
|
||||
}
|
||||
else if (mode == 0x01){
|
||||
strcpy_P(s_mode,COOL_P);
|
||||
}
|
||||
else if (mode == 0x02){
|
||||
strcpy_P(s_mode,HEAT_P);
|
||||
}
|
||||
else if (mode == 0x03){
|
||||
strcpy_P(s_mode,FAN_ONLY_P);
|
||||
}
|
||||
else if (mode == 0x04){
|
||||
strcpy_P(s_mode,DRY_P);
|
||||
}
|
||||
|
||||
publishTopic(item->itemArr->name, (long) mode, "/mode");
|
||||
|
||||
if (power & 0x01)
|
||||
publishTopic(item->itemArr->name, s_mode,"/cmd");
|
||||
|
||||
else publishTopic(item->itemArr->name, "OFF","/cmd");
|
||||
|
||||
String raw_str;
|
||||
char raw[75];
|
||||
for (int i=0; i < 37; i++){
|
||||
if (data[i] < 10){
|
||||
raw_str += "0";
|
||||
raw_str += String(data[i], HEX);
|
||||
} else {
|
||||
raw_str += String(data[i], HEX);
|
||||
}
|
||||
}
|
||||
raw_str.toUpperCase();
|
||||
raw_str.toCharArray(raw,75);
|
||||
publishTopic(item->itemArr->name, raw,"/raw");
|
||||
Serial.println(raw);
|
||||
|
||||
///////////////////////////////////
|
||||
}
|
||||
|
||||
byte getCRC(byte req[], size_t size){
|
||||
byte crc = 0;
|
||||
for (int i=2; i < size; i++){
|
||||
crc += req[i];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void SendData(byte req[], size_t size){
|
||||
AC_Serial.write(req, size - 1);
|
||||
AC_Serial.write(getCRC(req, size-1));
|
||||
AC_Serial.flush();
|
||||
}
|
||||
|
||||
inline unsigned char toHex( char ch ){
|
||||
return ( ( ch >= 'A' ) ? ( ch - 'A' + 0xA ) : ( ch - '0' ) ) & 0x0F;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int out_AC::Setup()
|
||||
{
|
||||
Serial.println("AC Init");
|
||||
AC_Serial.begin(9600);
|
||||
driverStatus = CST_INITIALIZED;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int out_AC::Stop()
|
||||
{
|
||||
Serial.println("AC De-Init");
|
||||
|
||||
driverStatus = CST_UNKNOWN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int out_AC::Status()
|
||||
{
|
||||
return driverStatus;
|
||||
}
|
||||
|
||||
int out_AC::isActive()
|
||||
{
|
||||
return (power & 1);
|
||||
}
|
||||
|
||||
int out_AC::Poll()
|
||||
{
|
||||
//debugSerial<<".";
|
||||
|
||||
long now = millis();
|
||||
if (now - prevPolling > INTERVAL_AC_POLLING) {
|
||||
prevPolling = now;
|
||||
Serial.println ("Polling");
|
||||
SendData(qstn, sizeof(qstn)/sizeof(byte)); //Опрос кондиционера
|
||||
}
|
||||
delay(100);
|
||||
if(AC_Serial.available() > 0){
|
||||
AC_Serial.readBytes(data, 37);
|
||||
while(AC_Serial.available()){
|
||||
delay(2);
|
||||
AC_Serial.read();
|
||||
}
|
||||
if (data[36] != inCheck){
|
||||
inCheck = data[36];
|
||||
InsertData(data, 37);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
int out_AC::Ctrl(short cmd, short n, int * Parameters, boolean send, int suffixCode, char* subItem)
|
||||
{
|
||||
// Some additional Subitems
|
||||
if (strcmp_P(subItem, LOCK_P) == 0) suffixCode = S_LOCK;
|
||||
else if (strcmp_P(subItem, SWING_P) == 0) suffixCode = S_SWING;
|
||||
else if (strcmp_P(subItem, QUIET_P) == 0) suffixCode = S_QUIET;
|
||||
else if (strcmp_P(subItem, RAW_P) == 0) suffixCode = S_RAW;
|
||||
|
||||
data[B_POWER] = power;
|
||||
// debugSerial<<F(".");
|
||||
switch(suffixCode)
|
||||
{
|
||||
case S_SET:
|
||||
set_tmp = Parameters[0]-16;
|
||||
if (set_tmp >= 0 && set_tmp <= 30)
|
||||
{
|
||||
data[B_SET_TMP] = set_tmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case S_CMD:
|
||||
switch (cmd)
|
||||
{
|
||||
case CMD_ON:
|
||||
data[B_POWER] |= 1;
|
||||
SendData(on, sizeof(on)/sizeof(byte));
|
||||
return 1;
|
||||
break;
|
||||
case CMD_OFF:
|
||||
case CMD_HALT:
|
||||
data[B_POWER] = 0;
|
||||
SendData(off, sizeof(off)/sizeof(byte));
|
||||
return 1;
|
||||
break;
|
||||
case CMD_AUTO:
|
||||
data[B_MODE] = 0;
|
||||
data[B_POWER] |= 1;
|
||||
break;
|
||||
case CMD_COOL:
|
||||
data[B_MODE] = 1;
|
||||
data[B_POWER] |= 1;
|
||||
break;
|
||||
case CMD_HEAT:
|
||||
data[B_MODE] = 2;
|
||||
data[B_POWER] |= 1;
|
||||
break;
|
||||
case CMD_DRY:
|
||||
data[B_MODE] = 4;
|
||||
data[B_POWER] |= 1;
|
||||
break;
|
||||
case CMD_FAN:
|
||||
data[B_MODE] = 3;
|
||||
data[B_POWER] |= 1;
|
||||
break;
|
||||
case CMD_UNKNOWN:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case S_FAN:
|
||||
data[B_FAN_SPD] = Parameters[0];
|
||||
break;
|
||||
|
||||
case S_MODE:
|
||||
data[B_MODE] = Parameters[0];
|
||||
break;
|
||||
|
||||
case S_LOCK:
|
||||
switch (cmd)
|
||||
{
|
||||
case CMD_ON:
|
||||
data[B_LOCK_REM] = 80;
|
||||
break;
|
||||
case CMD_OFF:
|
||||
data[B_LOCK_REM] = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case S_SWING:
|
||||
data[B_SWING] = Parameters[0];
|
||||
break;
|
||||
|
||||
case S_QUIET:
|
||||
switch (cmd)
|
||||
{
|
||||
case CMD_ON:
|
||||
data[B_POWER] |= 8;
|
||||
break;
|
||||
case CMD_OFF:
|
||||
data[B_POWER] &= ~8;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case S_RAW:
|
||||
/*
|
||||
{
|
||||
char buf[75];
|
||||
char hexbyte[3] = {0};
|
||||
strPayload.toCharArray(buf, 75);
|
||||
int octets[sizeof(buf) / 2] ;
|
||||
for (int i=0; i < 76; i += 2){
|
||||
hexbyte[0] = buf[i] ;
|
||||
hexbyte[1] = buf[i+1] ;
|
||||
data[i/2] = (toHex(hexbyte[0]) << 4) | toHex(hexbyte[1]);
|
||||
|
||||
|
||||
|
||||
AC_Serial.write(data, 37);
|
||||
AC_Serial.flush();
|
||||
|
||||
publishTopic("RAW", buf);
|
||||
}
|
||||
*/
|
||||
|
||||
break;
|
||||
|
||||
case S_NOTFOUND:
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
//////////
|
||||
if (strTopic == "/myhome/in/Conditioner/Fan_Speed"){
|
||||
if (strPayload == "max"){
|
||||
data[B_FAN_SPD] = 0;
|
||||
}
|
||||
if (strPayload == "mid"){
|
||||
data[B_FAN_SPD] = 1;
|
||||
}
|
||||
if (strPayload == "min"){
|
||||
data[B_FAN_SPD] = 2;
|
||||
}
|
||||
if (strPayload == "auto"){
|
||||
data[B_FAN_SPD] = 3;
|
||||
}
|
||||
}
|
||||
////////
|
||||
if (strTopic == "/myhome/in/Conditioner/Swing"){
|
||||
if (strPayload == "off"){
|
||||
data[B_SWING] = 0;
|
||||
}
|
||||
if (strPayload == "ud"){
|
||||
data[B_SWING] = 1;
|
||||
}
|
||||
if (strPayload == "lr"){
|
||||
data[B_SWING] = 2;
|
||||
}
|
||||
if (strPayload == "all"){
|
||||
data[B_SWING] = 3;
|
||||
}
|
||||
}
|
||||
////////
|
||||
if (strTopic == "/myhome/in/Conditioner/Lock_Remote"){
|
||||
if (strPayload == "true"){
|
||||
data[B_LOCK_REM] = 80;
|
||||
}
|
||||
if (strPayload == "false"){
|
||||
data[B_LOCK_REM] = 0;
|
||||
}
|
||||
}
|
||||
////////
|
||||
if (strTopic == "/myhome/in/Conditioner/Power"){
|
||||
if (strPayload == "off" || strPayload == "false" || strPayload == "0"){
|
||||
SendData(off, sizeof(off)/sizeof(byte));
|
||||
return;
|
||||
}
|
||||
if (strPayload == "on" || strPayload == "true" || strPayload == "1"){
|
||||
SendData(on, sizeof(on)/sizeof(byte));
|
||||
return;
|
||||
}
|
||||
if (strPayload == "quiet"){
|
||||
data[B_POWER] = 9;
|
||||
}
|
||||
}
|
||||
*/
|
||||
////////
|
||||
//if (strTopic == "/myhome/in/Conditioner/RAW")
|
||||
|
||||
data[B_CMD] = 0;
|
||||
data[9] = 1;
|
||||
data[10] = 77;
|
||||
data[11] = 95;
|
||||
SendData(data, sizeof(data)/sizeof(byte));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
37
lighthub/modules/out_ac.h
Normal file
37
lighthub/modules/out_ac.h
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef AC_DISABLE
|
||||
#include <abstractout.h>
|
||||
|
||||
#define LEN_B 37
|
||||
#define B_CUR_TMP 13 //Текущая температура
|
||||
#define B_CMD 17 // 00-команда 7F-ответ ???
|
||||
#define B_MODE 23 //04 - DRY, 01 - cool, 02 - heat, 00 - smart 03 - вентиляция
|
||||
#define B_FAN_SPD 25 //Скорость 02 - min, 01 - mid, 00 - max, 03 - auto
|
||||
#define B_SWING 27 //01 - верхний и нижний предел вкл. 00 - выкл. 02 - левый/правый вкл. 03 - оба вкл
|
||||
#define B_LOCK_REM 28 //80 блокировка вкл. 00 - выкл
|
||||
#define B_POWER 29 //on/off 01 - on, 00 - off (10, 11)-Компрессор??? 09 - QUIET
|
||||
#define B_FRESH 31 //fresh 00 - off, 01 - on
|
||||
#define B_SET_TMP 35 //Установленная температура
|
||||
|
||||
#define S_LOCK S_ADDITIONAL+1
|
||||
#define S_QUIET S_ADDITIONAL+2
|
||||
#define S_SWING S_ADDITIONAL+3
|
||||
#define S_RAW S_ADDITIONAL+4
|
||||
|
||||
extern void modbusIdle(void) ;
|
||||
class out_AC : public abstractOut {
|
||||
public:
|
||||
|
||||
out_AC(Item * _item):abstractOut(_item){};
|
||||
int Setup() override;
|
||||
int Poll() override;
|
||||
int Stop() override;
|
||||
int Status() override;
|
||||
int isActive() override;
|
||||
int Ctrl(short cmd, short n=0, int * Parameters=NULL, boolean send=true, int suffixCode=0, char* subItem=NULL) override;
|
||||
|
||||
protected:
|
||||
void InsertData(byte data[], size_t size);
|
||||
};
|
||||
#endif
|
||||
97
lighthub/modules/out_spiled.cpp
Normal file
97
lighthub/modules/out_spiled.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef SPILED_DISABLE
|
||||
|
||||
#include "modules/out_spiled.h"
|
||||
#include "Arduino.h"
|
||||
#include "options.h"
|
||||
#include "Streaming.h"
|
||||
#include "FastLED.h"
|
||||
#include "item.h"
|
||||
|
||||
#define NUM_LEDS 15
|
||||
#define DATA_PIN 4
|
||||
|
||||
static CRGB leds[NUM_LEDS];
|
||||
static int driverStatus = CST_UNKNOWN;
|
||||
|
||||
int out_SPILed::Setup()
|
||||
{
|
||||
Serial.println("SPI-LED Init");
|
||||
FastLED.addLeds<TM1809, DATA_PIN, BRG>(leds, NUM_LEDS);
|
||||
driverStatus = CST_INITIALIZED;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int out_SPILed::Stop()
|
||||
{
|
||||
Serial.println("SPI-LED De-Init");
|
||||
//FastLED.addLeds<TM1809, DATA_PIN, BRG>(leds, NUM_LEDS);
|
||||
FastLED.clear(true);
|
||||
driverStatus = CST_UNKNOWN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int out_SPILed::Status()
|
||||
{
|
||||
return driverStatus;
|
||||
}
|
||||
|
||||
int out_SPILed::isActive()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int out_SPILed::Poll()
|
||||
{
|
||||
//FastLED.show();
|
||||
return 1;
|
||||
};
|
||||
|
||||
int out_SPILed::Ctrl(short cmd, short n, int * Parameters, boolean send, int suffixCode, char* subItem)
|
||||
{
|
||||
int from=0, to=NUM_LEDS-1;
|
||||
|
||||
if (subItem)
|
||||
{ //Just single LED to control
|
||||
from=atoi(subItem);
|
||||
to=from;
|
||||
}
|
||||
debugSerial<<from<<F("-")<<to<<F(" cmd=")<<cmd<<endl;
|
||||
for (int i=from;i<=to;i++)
|
||||
{
|
||||
// debugSerial<<F(".");
|
||||
switch(cmd)
|
||||
{
|
||||
case CMD_ON:
|
||||
debugSerial<<F("Ch: ")<<i<<F(" White")<<endl;
|
||||
leds[i] = CRGB::White;
|
||||
break;
|
||||
case CMD_OFF:
|
||||
debugSerial<<F("Ch: ")<<i<<F(" Black")<<endl;
|
||||
leds[i] = CRGB::Black;
|
||||
break;
|
||||
case CMD_NUM:
|
||||
switch (suffixCode)
|
||||
{
|
||||
|
||||
// case S_POWER:
|
||||
// case S_VOL:
|
||||
//leds[n].setBrightness(Parameters[0]);
|
||||
// break;
|
||||
case S_SET:
|
||||
case S_HSV:
|
||||
debugSerial<<F("HSV: ")<<i<<F(" :")<<Parameters[0]<<Parameters[1]<<Parameters[2]<<endl;
|
||||
leds[i] = CHSV(Parameters[0],Parameters[1],Parameters[2]);
|
||||
break;
|
||||
case S_RGB:
|
||||
debugSerial<<F("RGB: ")<<i<<F(" :")<<Parameters[0]<<Parameters[1]<<Parameters[2]<<endl;
|
||||
leds[i] = CRGB(Parameters[0],Parameters[1],Parameters[2]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
FastLED.show();
|
||||
debugSerial<<F("Show ")<<endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
19
lighthub/modules/out_spiled.h
Normal file
19
lighthub/modules/out_spiled.h
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef SPILED_DISABLE
|
||||
#include <abstractout.h>
|
||||
|
||||
class out_SPILed : public abstractOut {
|
||||
public:
|
||||
|
||||
out_SPILed(Item * _item):abstractOut(_item){};
|
||||
int Setup() override;
|
||||
int Poll() override;
|
||||
int Stop() override;
|
||||
int Status() override;
|
||||
int isActive() override;
|
||||
int Ctrl(short cmd, short n=0, int * Parameters=NULL, boolean send=true, int suffixCode=0, char* subItem=NULL) override;
|
||||
|
||||
protected:
|
||||
};
|
||||
#endif
|
||||
@@ -18,7 +18,7 @@
|
||||
#define THERMO_OVERHEAT_CELSIUS 38.
|
||||
#define FM_OVERHEAT_CELSIUS 40.
|
||||
|
||||
#define MIN_VOLUME 10
|
||||
#define MIN_VOLUME 25
|
||||
#define INIT_VOLUME 50
|
||||
|
||||
#define MAXFLASHSTR 32
|
||||
|
||||
Reference in New Issue
Block a user