/* Copyright © 2017-2018 Andrey Klimov. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Homepage: http://lazyhome.ru GIT: https://github.com/anklimov/lighthub e-mail anklimov@gmail.com */ #include "options.h" #include "item.h" #include "aJSON.h" #include "utils.h" #include "textconst.h" #include "main.h" #ifdef _dmxout #include "dmx.h" #ifdef ADAFRUIT_LED #include #else #include "FastLED.h" #endif #endif #ifndef MODBUS_DISABLE #include #endif #include #include "modules/out_spiled.h" #include "modules/out_ac.h" #include "modules/out_motor.h" short modbusBusy = 0; extern aJsonObject *pollingItem; extern PubSubClient mqttClient; extern int8_t ethernetIdleCount; extern int8_t configLocked; extern lan_status lanStatus; static unsigned long lastctrl = 0; static aJsonObject *lastobj = NULL; int retrieveCode(char **psubItem); int txt2cmd(char *payload) { int cmd = CMD_UNKNOWN; // Check for command if (*payload == '-' || (*payload >= '0' && *payload <= '9')) cmd = CMD_NUM; else if (*payload == '%') cmd = CMD_UP; else if (strcmp_P(payload, ON_P) == 0) cmd = CMD_ON; else if (strcmp_P(payload, OFF_P) == 0) cmd = CMD_OFF; else if (strcmp_P(payload, REST_P) == 0) cmd = CMD_RESTORE; else if (strcmp_P(payload, TOGGLE_P) == 0) cmd = CMD_TOGGLE; else if (strcmp_P(payload, HALT_P) == 0) cmd = CMD_HALT; else if (strcmp_P(payload, XON_P) == 0) cmd = CMD_XON; else if (strcmp_P(payload, XOFF_P) == 0) cmd = CMD_XOFF; else if (strcmp_P(payload, HEAT_P) == 0) cmd = CMD_HEAT; else if (strcmp_P(payload, COOL_P) == 0) cmd = CMD_COOL; else if (strcmp_P(payload, AUTO_P) == 0) cmd = CMD_AUTO; else if (strcmp_P(payload, FAN_ONLY_P) == 0) cmd = CMD_FAN; else if (strcmp_P(payload, DRY_P) == 0) cmd = CMD_DRY; else if (strcmp_P(payload, TRUE_P) == 0) cmd = CMD_ON; else if (strcmp_P(payload, FALSE_P) == 0) cmd = CMD_OFF; else if (strcmp_P(payload, ENABLED_P) == 0) cmd = CMD_ON; else if (strcmp_P(payload, DISABLED_P) == 0) cmd = CMD_OFF; else if (strcmp_P(payload, INCREASE_P) == 0) cmd = CMD_UP; else if (strcmp_P(payload, DECREASE_P) == 0) cmd = CMD_DN; else if (strcmp_P(payload, HIGH_P) == 0) cmd = CMD_HIGH; else if (strcmp_P(payload, MED_P) == 0) cmd = CMD_MED; else if (strcmp_P(payload, LOW_P) == 0) cmd = CMD_LOW; else if (*payload == '{') cmd = CMD_JSON; else if (*payload == '#') cmd = CMD_RGB; else if (strncmp_P(payload, HSV_P, strlen (HSV_P)) == 0) cmd = CMD_HSV; else if (strncmp_P(payload, RGB_P, strlen (RGB_P)) == 0) cmd = CMD_RGB; return cmd; } int subitem2cmd(char *payload) { int cmd = 0; // Check for command if (payload) if (strcmp_P(payload, ON_P) == 0) cmd = CMD_ON; else if (strcmp_P(payload, OFF_P) == 0) cmd = CMD_OFF; //else if (strcmp_P(payload, REST_P) == 0) cmd = CMD_RESTORE; //else if (strcmp_P(payload, TOGGLE_P) == 0) cmd = CMD_TOGGLE; else if (strcmp_P(payload, HALT_P) == 0) cmd = CMD_HALT; else if (strcmp_P(payload, XON_P) == 0) cmd = CMD_XON; //else if (strcmp_P(payload, XOFF_P) == 0) cmd = CMD_XOFF; else if (strcmp_P(payload, HEAT_P) == 0) cmd = CMD_HEAT; else if (strcmp_P(payload, COOL_P) == 0) cmd = CMD_COOL; else if (strcmp_P(payload, AUTO_P) == 0) cmd = CMD_AUTO; else if (strcmp_P(payload, FAN_ONLY_P) == 0) cmd = CMD_FAN; else if (strcmp_P(payload, DRY_P) == 0) cmd = CMD_DRY; //else if (strcmp_P(payload, HIGH_P) == 0) cmd = CMD_HIGH; //else if (strcmp_P(payload, MED_P) == 0) cmd = CMD_MED; //else if (strcmp_P(payload, LOW_P) == 0) cmd = CMD_LOW; return cmd; } int txt2subItem(char *payload) { int cmd = S_NOTFOUND; if (!payload || !strlen(payload)) return S_NOTFOUND; // Check for command if (strcmp_P(payload, SET_P) == 0) cmd = S_SET; else if (strcmp_P(payload, CMD_P) == 0) cmd = S_CMD; else if (strcmp_P(payload, MODE_P) == 0) cmd = S_MODE; else if (strcmp_P(payload, HSV_P) == 0) cmd = S_HSV; else if (strcmp_P(payload, RGB_P) == 0) cmd = S_RGB; else if (strcmp_P(payload, FAN_P) == 0) cmd = S_FAN; else if (strcmp_P(payload, HUE_P) == 0) cmd = S_HUE; else if (strcmp_P(payload, SAT_P) == 0) cmd = S_SAT; /* UnUsed now else if (strcmp_P(payload, SETPOINT_P) == 0) cmd = S_SETPOINT; else if (strcmp_P(payload, TEMP_P) == 0) cmd = S_TEMP; else if (strcmp_P(payload, POWER_P) == 0) cmd = S_POWER; else if (strcmp_P(payload, VOL_P) == 0) cmd = S_VOL; */ return cmd; } const short defval[4] = {0, 0, 0, 0}; //Type,Arg,Val,Cmd Item::Item(aJsonObject *obj)//Constructor { itemArr = obj; driver = NULL; Parse(); } void Item::Parse() { if (isValid()) { // Todo - avoid static enlarge for every types for (int i = aJson.getArraySize(itemArr); i < 4; i++) aJson.addItemToArray(itemArr, aJson.createItem( int(defval[i]))); //Enlarge item to 4 elements. VAL=int if no other definition in conf itemType = aJson.getArrayItem(itemArr, I_TYPE)->valueint; itemArg = aJson.getArrayItem(itemArr, I_ARG); itemVal = aJson.getArrayItem(itemArr, I_VAL); itemExt = aJson.getArrayItem(itemArr, I_EXT); switch (itemType) { #ifndef SPILED_DISABLE case CH_SPILED: driver = new out_SPILed (this); // debugSerial<name << F(" T:") << itemType << F(" =") << getArg() << endl; } } boolean Item::Setup() { if (driver) { if (driver->Status()) driver->Stop(); driver->Setup(); return true; } else return false; } Item::~Item() { if (driver) { delete driver; // debugSerial<valueint & CMD_MASK; else return -1; } void Item::setCmd(uint8_t cmdValue) { aJsonObject *itemCmd = aJson.getArrayItem(itemArr, I_CMD); if (itemCmd) { itemCmd->valueint = cmdValue & CMD_MASK | itemCmd->valueint & FLAG_MASK; // Preserve special bits debugSerial<valueint & flag & FLAG_MASK; } } void Item::setFlag (short flag) { aJsonObject *itemCmd = aJson.getArrayItem(itemArr, I_CMD); if (itemCmd) { itemCmd->valueint |= flag & FLAG_MASK; // Preserve CMD bits debugSerial<valueint &= CMD_MASK | ~(flag & FLAG_MASK); // Preserve CMD bits debugSerial<type == aJson_Int){ if (!n) return itemArg->valueint; else return 0;//-1; } if ((itemArg->type == aJson_Array) && ( n < aJson.getArraySize(itemArg))) return aJson.getArrayItem(itemArg, n)->valueint; else return 0;//-2; } /* int Item::getVal(short n) //Return Val from Value array { if (!itemVal) return -1; else if (itemVal->type==aJson_Array) { aJsonObject *t = aJson.getArrayItem(itemVal,n); if (t) return t->valueint; else return -3; } else return -2; } */ long int Item::getVal() //Return Val if val is int or first elem of Value array { if (!itemVal) return 0;//-1; if (itemVal->type == aJson_Int) return itemVal->valueint; else if (itemVal->type == aJson_Array) { aJsonObject *t = aJson.getArrayItem(itemVal, 0); if (t) return t->valueint; else return 0;//-3; } else return 0;//-2; } /* void Item::setVal(short n, int par) // Only store if VAL is array defined in config to avoid waste of RAM { if (!itemVal || itemVal->type!=aJson_Array) return; debugSerial<type == aJson_Array)); } /* void Item::copyPar (aJsonObject *itemV) { int n=aJson.getArraySize(itemV); //for (int i=aJson.getArraySize(itemVal);ivalueint); } */ #if defined(ARDUINO_ARCH_ESP32) void analogWrite(int pin, int val) { //TBD } #endif /* boolean Item::getEnableCMD(int delta) { return ((millis() - lastctrl > (unsigned long) delta));// || ((void *)itemArr != (void *) lastobj)); } */ // If retrieving subitem code ok - return it // parameter will point on the rest truncated part of subitem // or pointer to NULL of whole string converted to subitem code int retrieveCode(char **psubItem) { int suffixCode; char* suffix; //debugSerial<"); } debugSerial<0)) { debugSerial<0) cmd = CMD_OFF; else cmd = CMD_ON; break; case CMD_RESTORE: if (itemType != CH_GROUP) //individual threating of channels. Ignore restore command for groups switch (t = getCmd()) { case CMD_HALT: //previous command was HALT ? debugSerial << F("Restored from:") << t << endl; if (itemType == CH_THERMO) cmd = CMD_AUTO; else cmd = CMD_ON; //turning on break; default: return -3; } break; case CMD_XOFF: if (itemType != CH_GROUP) //individual threating of channels. Ignore restore command for groups switch (t = getCmd()) { case CMD_XON: //previous command was CMD_XON ? debugSerial << F("Turned off from:") << t << endl; cmd = CMD_OFF; //turning Off break; default: debugSerial<Ctrl(CMD_ON, n, Par, send, suffixCode, subItem); setCmd(CMD_XON); } else { //cmd = CMD_ON; debugSerial<0) //if channel was active before CMD_HALT { res = driver->Ctrl(CMD_OFF, n, Par, send, suffixCode, subItem); setCmd(CMD_HALT); return res; } else { debugSerial<Ctrl(cmd, n, Par, send, suffixCode, subItem); setCmd(CMD_OFF); } else { debugSerial<Ctrl(cmd, n, Parameters, send, suffixCode, subItem); break; */ default: res = driver->Ctrl(cmd, n, Par, send, suffixCode, subItem); if (cmd) setCmd(cmd); } return res; } // Legacy monolite core code bool toExecute = (chActive>0); //if channel is already active - unconditionally propogate changes switch (cmd) { case 0: // No command - set params if (!suffixCode) toExecute= true; if (suffixCode == S_HUE || suffixCode == S_SAT) break; switch (itemType) { case CH_RGBW: //only if configured VAL array if (!Par[1] && (n == 3)) itemType = CH_WHITE; case CH_RGB: case CH_GROUP: //Save for groups as well st.aslong = getVal(); switch (n) { case 1: st.v = Par[0]; //Volume only if (st.hsv_flag) { Par[0] = st.h; Par[1] = st.s; Par[2] = st.v; n = 3;} break; case 2: // Just hue and saturation st.h = Par[0]; st.s = Par[1]; Par[2] = st.v; st.hsv_flag = 1; n = 3; break; case 3: //complete triplet st.h = Par[0]; st.s = Par[1]; st.v = Par[2]; st.hsv_flag = 1; } setVal(st.aslong); if (!suffixCode) { // if (chActive>0 && !st.v) { setCmd(CMD_OFF); SendStatus(SEND_COMMAND | SEND_PARAMETERS | SEND_DEFFERED); } else if (chActive==0 && st.v) { setCmd(CMD_ON); SendStatus(SEND_COMMAND | SEND_PARAMETERS | SEND_DEFFERED); } //// else setCmd(0); SendStatus(SEND_PARAMETERS | SEND_DEFFERED); } else { //// setCmd(0); SendStatus(SEND_PARAMETERS | SEND_DEFFERED); } break; case CH_PWM: case CH_VC: case CH_DIMMER: case CH_MODBUS: setVal(Par[0]); // Store value // setCmd(cmd2changeActivity(chActive,cmd)); if (!suffixCode) { // Not restoring, working if (chActive>0 && !Par[0]) setCmd(CMD_OFF); if (chActive==0 && Par[0]) setCmd(CMD_ON); SendStatus(SEND_COMMAND | SEND_PARAMETERS | SEND_DEFFERED); // Send back parameter for channel above this line } else SendStatus(SEND_PARAMETERS | SEND_DEFFERED); break; case CH_VCTEMP: // moved case CH_THERMO: ///? wasnt send before/ now will /// setVal(Par[0]); // Store value if (send) SendStatus(SEND_PARAMETERS | SEND_DEFFERED); // Send back parameter for channel above this line }//itemtype if (! toExecute && itemType !=CH_GROUP) return 1; // Parameters are stored, no further action required break; case CMD_XON: if (!chActive>0) //if channel was'nt active before CMD_XON { debugSerial<0 && send) { SendStatus(SEND_COMMAND); return 1; } { short params = 0; //retrive stored values st.aslong = getVal(); // If command is ON but saved volume to low - setup mimimum volume switch (itemType) { case CH_DIMMER: case CH_MODBUS: if (st.aslong0) DmxWrite(iaddr, map(Par[0], 0, 100, 0, 255)); break; case CH_RGBW: //Colour RGBW // Saturation 0 - Only white // 0..50 - white + RGB //50..100 RGB { // int k; if (Par[1]<50 && iaddr>0) { // Using white DmxWrite(iaddr + 3, map((50 - Par[1]) * Par[2], 0, 5000, 0, 255)); int rgbvLevel = map (Par[1],0,50,0,255*2); rgbValue = map(Par[2], 0, 100, 0, rgbvLevel); rgbSaturation = map(Par[1], 0, 50, 255, 100); if (rgbValue>255) rgbValue = 255; } else { //rgbValue = map(Par[2], 0, 100, 0, 255); rgbSaturation = map(Par[1], 50, 100, 100, 255); if (iaddr>0) DmxWrite(iaddr + 3, 0); } //DmxWrite(iaddr + 3, k = map((100 - Par[1]) * Par[2], 0, 10000, 0, 255)); //debugSerial<0) { #ifdef ADAFRUIT_LED Adafruit_NeoPixel strip(0, 0, 0); uint32_t rgb = strip.ColorHSV(map(Par[0], 0, 365, 0, 65535), rgbSaturation, rgbValue); DmxWrite(iaddr, (rgb >> 16)& 0xFF); DmxWrite(iaddr + 1, (rgb >> 8) & 0xFF); DmxWrite(iaddr + 2, rgb & 0xFF); #else CRGB rgb = CHSV(map(Par[0], 0, 365, 0, 255), rgbSaturation, rgbValue); DmxWrite(iaddr, rgb.r); DmxWrite(iaddr + 1, rgb.g); DmxWrite(iaddr + 2, rgb.b); #endif break; } case CH_WHITE: if (iaddr>0) { DmxWrite(iaddr, 0); DmxWrite(iaddr + 1, 0); DmxWrite(iaddr + 2, 0); DmxWrite(iaddr + 3, map(Par[2], 0, 100, 0, 255)); break; } #endif #ifdef _modbus case CH_MODBUS: modbusDimmerSet(Par[0]); break; #endif case CH_GROUP://Group { if (itemArg->type == aJson_Array) { aJsonObject *i = itemArg->child; configLocked++; while (i) { if (i->type == aJson_String) { Item it(i->valuestring); it.Ctrl(cmd, n, Par, send,suffixCode,subItem); //// was true } i = i->next; } //while configLocked--; } //if } //case break; case CH_RELAY: if (iaddr) { int k; short inverse = 0; if (iaddr < 0) { iaddr = -iaddr; inverse = 1; } pinMode(iaddr, OUTPUT); if (inverse) digitalWrite(iaddr, k = ((cmd == CMD_ON || cmd == CMD_XON) ? LOW : HIGH)); else digitalWrite(iaddr, k = ((cmd == CMD_ON || cmd == CMD_XON) ? HIGH : LOW)); debugSerial< PWM frequency is 31000 Hz //prescaler = 2 ---> PWM frequency is 4000 Hz //prescaler = 3 ---> PWM frequency is 490 Hz (default value) //prescaler = 4 ---> PWM frequency is 120 Hz //prescaler = 5 ---> PWM frequency is 30 Hz //prescaler = 6 ---> PWM frequency is <20 Hz #if defined(__AVR_ATmega2560__) int tval = 7; // this is 111 in binary and is used as an eraser TCCR4B &= ~tval; // this operation (AND plus NOT), set the three bits in TCCR2B to 0 TCCR3B &= ~tval; tval = 2; TCCR4B |= tval; TCCR3B |= tval; #endif if (inverse) k = map(Par[0], 100, 0, 0, 255); else k = map(Par[0], 0, 100, 0, 255); analogWrite(iaddr, k); debugSerial<valuestring); // if (it.isValid() && it.itemType == CH_VC) VacomSetHeat(Par[0], cmd); break; } #endif } } int Item::isActive() { CHstore st; int val = 0; debugSerial<name; if (!isValid()) { debugSerial<isActive(); st.aslong = getVal(); switch (itemType) { case CH_GROUP: //make recursive calculation - is it some active in group if (itemArg->type == aJson_Array) { debugSerial<child; while (i) { if (i->type == aJson_String) { Item it(i->valuestring); if (it.isValid() && it.isActive()>0) { debugSerial<next; } //while debugSerial<type==aJson_Array) && (aJson.getArrayItem(item, I_TYPE)->valueint==CH_THERMO)) { for (int i=aJson.getArraySize(item);i<4;i++) aJson.addItemToArray(item,aJson.createItem(int(0))); //Enlarge item to 4 elements if (!cmd) aJson.getArrayItem(item, I_VAL)->valueint=t; aJson.getArrayItem(item, I_CMD)->valueint=cmd; } } } void PooledItem::Idle() { if (PoolingInterval) { Pool(); next=millis()+PoolingInterval; } }; addr 10d Снять аварию 42001 (2001=7d1) =>4 [22:20:33] Write task has completed successfully [22:20:33] <= Response: 0A 06 07 D0 00 04 89 FF [22:20:32] => Poll: 0A 06 07 D0 00 04 89 FF 100% 2003-> 10000 [22:24:05] Write task has completed successfully [22:24:05] <= Response: 0A 06 07 D2 27 10 33 C0 [22:24:05] => Poll: 0A 06 07 D2 27 10 33 C0 ON 2001->1 [22:24:50] Write task has completed successfully [22:24:50] <= Response: 0A 06 07 D0 00 01 49 FC [22:24:50] => Poll: 0A 06 07 D0 00 01 49 FC OFF 2001->0 [22:25:35] Write task has completed successfully [22:25:35] <= Response: 0A 06 07 D0 00 00 88 3C [22:25:34] => Poll: 0A 06 07 D0 00 00 88 3C POLL 2101x10 [22:27:29] <= Response: 0A 03 14 00 23 00 00 27 10 13 88 0B 9C 00 32 00 F8 00 F2 06 FA 01 3F AD D0 [22:27:29] => poll: 0A 03 08 34 00 0A 87 18 */ #ifdef _modbus int Item::modbusDimmerSet(uint16_t value) { switch (getCmd()) { case CMD_OFF: case CMD_HALT: value=0; break; } short numpar=0; if ((itemArg->type == aJson_Array) && ((numpar = aJson.getArraySize(itemArg)) >= 2)) { int _addr = aJson.getArrayItem(itemArg, MODBUS_CMD_ARG_ADDR)->valueint; int _reg = aJson.getArrayItem(itemArg, MODBUS_CMD_ARG_REG)->valueint; int _mask = -1; if (numpar >= (MODBUS_CMD_ARG_MASK+1)) _mask = aJson.getArrayItem(itemArg, MODBUS_CMD_ARG_MASK)->valueint; int _maxval = 0x3f; if (numpar >= (MODBUS_CMD_ARG_MAX_SCALE+1)) _maxval = aJson.getArrayItem(itemArg, MODBUS_CMD_ARG_MAX_SCALE)->valueint; int _regType = MODBUS_HOLDING_REG_TYPE; if (numpar >= (MODBUS_CMD_ARG_REG_TYPE+1)) _regType = aJson.getArrayItem(itemArg, MODBUS_CMD_ARG_REG_TYPE)->valueint; if (_maxval) return modbusDimmerSet(_addr, _reg, _regType, _mask, map(value, 0, 100, 0, _maxval)); else return modbusDimmerSet(_addr, _reg, _regType, _mask, value); } } #endif void Item::mb_fail() { debugSerial<")<<_HEX(_reg)<name, sizeof(addrstr) - 1); strncat(addrstr, "_stat", sizeof(addrstr) - 1); // aJson.addStringToObject(out,"type", "rect"); modbusSerial.begin(9600, fmPar); node.begin(getArg(), modbusSerial); result = node.readHoldingRegisters(2101 - 1, 10); // do something with data if read is successful if (result == node.ku8MBSuccess) { debugSerial<type == aJson_Array) { aJsonObject *airGateObj = aJson.getArrayItem(itemArg, 1); if (airGateObj && airGateObj->type == aJson_String) { int val = 100; Item item(airGateObj->valuestring); if (item.isValid()) item.Ctrl(0, 1, &val); } } } else debugSerial << F("Modbus polling error=") << _HEX(result) << endl; if (node.getResponseBuffer(0) & 8) //Active fault { result = node.readHoldingRegisters(2111 - 1, 1); if (result == node.ku8MBSuccess) aJson.addNumberToObject(out, "flt", (int) node.getResponseBuffer(0)); modbusBusy=0; if (isActive()>0) Ctrl(CMD_OFF); //Shut down /// modbusBusy=1; } else aJson.addNumberToObject(out, "flt", 0); delay(50); result = node.readHoldingRegisters(20 - 1, 4); // do something with data if read is successful if (result == node.ku8MBSuccess) { debugSerial << F(" PI Val :"); for (j = 0; j < 4; j++) { data = node.getResponseBuffer(j); debugSerial << data << F("-"); } debugSerial << endl; int set = node.getResponseBuffer(0); float ftemp, fset = set * a + b; if (set) aJson.addNumberToObject(out, "set", fset); aJson.addNumberToObject(out, "t", ftemp = (int) node.getResponseBuffer(1) * a + b); // aJson.addNumberToObject(out,"d", (int) node.getResponseBuffer(2)*a+b); int16_t pwr = node.getResponseBuffer(3); if (pwr > 0) aJson.addNumberToObject(out, "pwr", pwr / 10.); else aJson.addNumberToObject(out, "pwr", 0); if (ftemp > FM_OVERHEAT_CELSIUS && set) { if (mqttClient.connected() && !ethernetIdleCount) mqttClient.publish("/alarm/ovrht", itemArr->name); Ctrl(CMD_OFF); //Shut down } } else debugSerial << F("Modbus polling error=") << _HEX(result); outch = aJson.print(out); if (mqttClient.connected() && !ethernetIdleCount) mqttClient.publish(addrstr, outch); free(outch); aJson.deleteItem(out); modbusBusy = 0; } boolean Item::checkModbusRetry() { if (modbusBusy) return false; // int cmd = getCmd(); if (getFlag(SEND_RETRY)) { // if last sending attempt of command was failed int val = getVal(); debugSerial<type != aJson_Array) || ((numpar = aJson.getArraySize(itemArg)) < 2)) { debugSerial<= (MODBUS_CMD_ARG_REG_TYPE+1)) _regType = aJson.getArrayItem(itemArg, MODBUS_CMD_ARG_REG_TYPE)->valueint; // short mask = getArg(2); // debugSerial<")); // debugSerial<name << F(" Val: ") << _HEX(data) << endl; checkModbusDimmer(data); // Looking 1 step ahead for modbus item, which uses same register Item nextItem(pollingItem->next); if (pollingItem && nextItem.isValid() && nextItem.itemType == CH_MODBUS && nextItem.getArg(0) == addr && nextItem.getArg(1) == reg) { nextItem.checkModbusDimmer(data); pollingItem = pollingItem->next; if (!pollingItem) pollingItem = items->child; } } else debugSerial << F("Modbus polling error=") << _HEX(result) << endl; modbusBusy = 0; } int Item::checkModbusDimmer(int data) { short mask = getArg(2); if (mask < 0) return 0; short maxVal = getArg(3); if (maxVal<=0) maxVal = 0x3f; int d = data; if (mask == 1) d >>= 8; if (mask == 0 || mask == 1) d &= 0xff; if (maxVal) d = map(d, 0, maxVal, 0, 100); int cmd = getCmd(); //debugSerial<Status()) { return driver->Poll(cause); } return INTERVAL_POLLING; } void Item::sendDelayedStatus() { long int flags = getFlag(SEND_COMMAND | SEND_PARAMETERS); // debugSerial<name<getChanType(); //retrive stored values st.aslong = getVal(); switch (chanType) { //case CH_GROUP: case CH_RGBW: case CH_RGB: snprintf(valstr, sizeof(valstr), "%d,%d,%d", st.h,st.s,st.v); break; case CH_GROUP: if (st.hsv_flag) snprintf(valstr, sizeof(valstr), "%d,%d,%d", st.h,st.s,st.v); else snprintf(valstr, sizeof(valstr), "%d", st.v); break; case CH_RELAY: sendFlags &= ~SEND_PARAMETERS; //No need to send value for relay break; default: snprintf(valstr, sizeof(valstr), "%d", st.aslong); }//itemtype } if (sendFlags & SEND_COMMAND) { // Preparing Command payload ////////////// switch (chancmd) { case CMD_ON: case CMD_XON: case CMD_AUTO: case CMD_HEAT: case CMD_COOL: strcpy_P(cmdstr, ON_P); break; case CMD_OFF: case CMD_HALT: strcpy_P(cmdstr, OFF_P); break; case 0: /// case CMD_SET: sendFlags &= ~SEND_COMMAND; // Not send command for parametrized req break; default: debugSerial<name, sizeof(addrstr)); if (mqttClient.connected() && !ethernetIdleCount) { if (sendFlags & SEND_PARAMETERS && chancmd != CMD_OFF && chancmd != CMD_HALT) { mqttClient.publish(addrstr, valstr, true); debugSerial<")<")<name, sizeof(addrstr)); strncat(addrstr, "/", sizeof(addrstr)); strncat_P(addrstr, SET_P, sizeof(addrstr)); debugSerial<")<name, sizeof(addrstr)); strncat(addrstr, "/", sizeof(addrstr)); strncat_P(addrstr, CMD_P, sizeof(addrstr)); debugSerial<")<getChanType(); return itemType; } /////////////////////////////////////////