Merge pull request #8251 from pcdiem/device-groups-9

Add DGR Command item and message logging
This commit is contained in:
Theo Arends 2020-04-22 09:06:02 +02:00 committed by GitHub
commit 14e19b7cde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 336 additions and 333 deletions

View File

@ -420,8 +420,8 @@
#define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code) #define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code)
// #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code) // #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code)
//#define USE_HOTPLUG // Add support for sensor HotPlug //#define USE_HOTPLUG // Add support for sensor HotPlug
#define USE_DEVICE_GROUPS // Add support for device groups (+5k code) #define USE_DEVICE_GROUPS // Add support for device groups (+5k6 code)
#define USE_DEVICE_GROUPS_SEND // Add support for the DevGroupSend command (+0k6 code) #define USE_DEVICE_GROUPS_SEND // Add support for the DevGroupSend command (+0k5 code)
#define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+2k5 code) #define USE_PWM_DIMMER // Add support for MJ-SD01/acenx/NTONPOWER PWM dimmers (+2k5 code)
#define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer, also adds device groups support (+1k code plus device groups size) #define USE_PWM_DIMMER_REMOTE // Add support for remote switches to PWM Dimmer, also adds device groups support (+1k code plus device groups size)
//#define USE_KEELOQ // Add support for Jarolift rollers by Keeloq algorithm (+4k5 code) //#define USE_KEELOQ // Add support for Jarolift rollers by Keeloq algorithm (+4k5 code)

View File

@ -41,6 +41,7 @@ struct device_group {
uint32_t next_announcement_time; uint32_t next_announcement_time;
uint32_t next_ack_check_time; uint32_t next_ack_check_time;
uint32_t member_timeout_time; uint32_t member_timeout_time;
uint16_t outgoing_sequence;
uint16_t last_full_status_sequence; uint16_t last_full_status_sequence;
uint16_t message_length; uint16_t message_length;
uint16_t ack_check_interval; uint16_t ack_check_interval;
@ -58,12 +59,13 @@ struct device_group {
}; };
struct device_group * device_groups; struct device_group * device_groups;
char * log_ptr;
int log_remaining;
uint32_t next_check_time = 0; uint32_t next_check_time = 0;
uint16_t outgoing_sequence = 0;
bool device_groups_initialized = false; bool device_groups_initialized = false;
bool device_groups_initialization_failed = false; bool device_groups_initialization_failed = false;
bool building_status_message = false; bool building_status_message = false;
bool processing_remote_device_message = false; bool ignore_dgr_sends = false;
bool udp_was_connected = false; bool udp_was_connected = false;
void DeviceGroupsInit(void) void DeviceGroupsInit(void)
@ -117,12 +119,21 @@ char * IPAddressToString(const IPAddress& ip_address)
return buffer; return buffer;
} }
void AddDeviceGroupLog(const char * format, ...) {
va_list ap;
va_start(ap, format);
int log_length = vsnprintf_P(log_ptr, log_remaining, format, ap);
va_end(ap);
log_ptr += log_length;
log_remaining -= log_length;
}
char * BeginDeviceGroupMessage(struct device_group * device_group, uint16_t flags, bool hold_sequence = false) char * BeginDeviceGroupMessage(struct device_group * device_group, uint16_t flags, bool hold_sequence = false)
{ {
char * message_ptr = &device_group->message[device_group->message_header_length]; char * message_ptr = &device_group->message[device_group->message_header_length];
if (!hold_sequence && !++outgoing_sequence) outgoing_sequence = 1; if (!hold_sequence && !++device_group->outgoing_sequence) device_group->outgoing_sequence = 1;
*message_ptr++ = outgoing_sequence & 0xff; *message_ptr++ = device_group->outgoing_sequence & 0xff;
*message_ptr++ = outgoing_sequence >> 8; *message_ptr++ = device_group->outgoing_sequence >> 8;
*message_ptr++ = flags & 0xff; *message_ptr++ = flags & 0xff;
*message_ptr++ = flags >> 8; *message_ptr++ = flags >> 8;
return message_ptr; return message_ptr;
@ -162,17 +173,241 @@ bool DeviceGroupItemShared(bool incoming, uint8_t item)
return (!mask || ((incoming ? Settings.device_group_share_in : Settings.device_group_share_out) & mask)); return (!mask || ((incoming ? Settings.device_group_share_in : Settings.device_group_share_out) & mask));
} }
void SendDeviceGroupPacket(IPAddress ip, char * packet, int len, const char * label) void SendReceiveDeviceGroupPacket(struct device_group * device_group, struct device_group_member * device_group_member, char * message, int message_length, bool received)
{ {
if (!ip) ip = IPAddress(239,255,255,250); char log_buffer[LOGSZ];
for (int attempt = 1; attempt <= 5; attempt++) { bool item_processed = false;
if (PortUdp.beginPacket(ip, 1900)) { uint16_t message_sequence;
PortUdp_write(packet, len); uint16_t flags;
if (PortUdp.endPacket()) return; int device_group_index = device_group - device_groups;
log_ptr = log_buffer;
log_remaining = sizeof(log_buffer);
AddDeviceGroupLog(PSTR("DGR: %s %s message"), (received ? PSTR("Received") : PSTR("Sending")), device_group->group_name);
if (received || device_group_member) AddDeviceGroupLog(PSTR(" %s %s"), (received ? PSTR("from") : PSTR("to")), (device_group_member ? IPAddressToString(device_group_member->ip_address) : PSTR("local")));
// Find the start of the actual message (after the http header).
char * message_end_ptr = message + message_length;
char * message_ptr = strstr_P(message, PSTR("\n\n"));
if (message_ptr == nullptr) return;
message_ptr += 2;
// Get the message sequence and flags.
if (message_ptr + 4 > message_end_ptr) goto badmsg; // Malformed message - must be at least 16-bit sequence, 16-bit flags left
message_sequence = *message_ptr++;
message_sequence |= *message_ptr++ << 8;
flags = *message_ptr++;
flags |= *message_ptr++ << 8;
AddDeviceGroupLog(PSTR(": seq=%u, flags=%u"), message_sequence, flags);
// If this is an announcement, just log it.
if (flags == DGR_FLAG_ANNOUNCEMENT) goto write_log;
// If this is a received ack message, save the message sequence if it's newwer than the last ack
// we received from this member.
if (flags == DGR_FLAG_ACK) {
if (received && device_group_member && (message_sequence > device_group_member->acked_sequence || device_group_member->acked_sequence - message_sequence < 64536)) {
device_group_member->acked_sequence = message_sequence;
} }
delay(10); goto write_log;
}
// If this is a received message, send an ack message to the sender.
if (device_group_member) {
if (received) {
if (!(flags & DGR_FLAG_MORE_TO_COME)) {
*(message_ptr - 2) = DGR_FLAG_ACK;
*(message_ptr - 1) = 0;
SendReceiveDeviceGroupPacket(device_group, device_group_member, message, message_ptr - message, false);
}
}
// If we're sending this message directly to a member, it's a resend of the lst message.
else {
AddDeviceGroupLog(PSTR(", last ack=%u"), device_group_member->acked_sequence);
goto write_log;
}
}
// If this is a status request message, skip item processing.
if ((flags & DGR_FLAG_STATUS_REQUEST)) goto write_log;
// If this is a received message, ...
if (received) {
// If we already processed this or a later message from this group member, ignore this message.
if (device_group_member) {
if (message_sequence <= device_group_member->received_sequence) {
if (message_sequence == device_group_member->received_sequence || device_group_member->received_sequence - message_sequence > 64536) {
AddDeviceGroupLog(PSTR(" (old)"));
goto write_log;
}
}
device_group_member->received_sequence = message_sequence;
}
/*
XdrvMailbox
bool grpflg
bool usridx
uint16_t command_code Item code
uint32_t index 0:15 Flags, 16:31 Message sequence
uint32_t data_len String item value length
int32_t payload Integer item value
char *topic Pointer to device group index
char *data Pointer to non-integer item value
char *command nullptr
*/
XdrvMailbox.command = nullptr; // Indicates the source is a device group update
XdrvMailbox.index = flags | message_sequence << 16;
XdrvMailbox.topic = (char *)&device_group_index;
if (flags & (DGR_FLAG_MORE_TO_COME | DGR_FLAG_DIRECT)) skip_light_fade = true;
// Set the flag to ignore device group send message request so callbacks from the drivers do not
// send updates.
ignore_dgr_sends = true;
}
uint8_t item;
int32_t value;
for (;;) {
if (message_ptr >= message_end_ptr) goto badmsg; // Malformed message
item = *message_ptr++;
if (!item) break; // Done
#ifdef DEVICE_GROUPS_DEBUG
switch (item) {
case DGR_ITEM_LIGHT_FADE:
case DGR_ITEM_LIGHT_SPEED:
case DGR_ITEM_LIGHT_BRI:
case DGR_ITEM_LIGHT_SCHEME:
case DGR_ITEM_LIGHT_FIXED_COLOR:
case DGR_ITEM_BRI_PRESET_LOW:
case DGR_ITEM_BRI_PRESET_HIGH:
case DGR_ITEM_BRI_POWER_ON:
case DGR_ITEM_POWER:
case DGR_ITEM_EVENT:
case DGR_ITEM_LIGHT_CHANNELS:
break;
default:
AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: *** Invalid item=%u"), item);
}
#endif // DEVICE_GROUPS_DEBUG
AddDeviceGroupLog(PSTR(", %u="), item);
if (received) XdrvMailbox.data = message_ptr;
if (item <= DGR_ITEM_LAST_32BIT) {
value = *message_ptr++;
if (item > DGR_ITEM_MAX_8BIT) {
value |= *message_ptr++ << 8;
if (item > DGR_ITEM_MAX_16BIT) {
value |= *message_ptr++ << 16;
value |= *message_ptr++ << 24;
#ifdef USE_DEVICE_GROUPS_SEND
device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = (item == DGR_ITEM_POWER ? value & 0xffffff : value);
#endif // USE_DEVICE_GROUPS_SEND
}
#ifdef USE_DEVICE_GROUPS_SEND
else {
device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = value;
}
#endif // USE_DEVICE_GROUPS_SEND
}
#ifdef USE_DEVICE_GROUPS_SEND
else {
device_group->values_8bit[item] = value;
}
#endif // USE_DEVICE_GROUPS_SEND
AddDeviceGroupLog(PSTR("%u"), value);
}
else if (item <= DGR_ITEM_MAX_STRING) {
value = strlen(message_ptr);
if (message_ptr + value >= message_end_ptr) goto badmsg; // Malformed message
AddDeviceGroupLog(PSTR("'%s'"), message_ptr);
message_ptr += value + 1;
}
else {
switch (item) {
case DGR_ITEM_LIGHT_CHANNELS:
AddDeviceGroupLog(PSTR("%08X%02X"), *(uint32_t *)message_ptr, *(message_ptr + 4));
message_ptr += 5;
break;
}
}
if (received && DeviceGroupItemShared(true, item)) {
item_processed = true;
XdrvMailbox.command_code = item;
XdrvMailbox.payload = value;
XdrvMailbox.data_len = value;
AddDeviceGroupLog(PSTR("*"));
switch (item) {
case DGR_ITEM_POWER:
if (device_group->local) {
uint8_t mask_devices = value >> 24;
if (mask_devices > devices_present) mask_devices = devices_present;
for (uint32_t i = 0; i < devices_present; i++) {
uint32_t mask = 1 << i;
bool on = (value & mask);
if (on != (power & mask)) ExecuteCommandPower(i + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE);
}
}
break;
case DGR_ITEM_EVENT:
CmndEvent();
break;
case DGR_ITEM_COMMAND:
ExecuteCommand(XdrvMailbox.data, SRC_REMOTE);
break;
}
XdrvCall(FUNC_DEVICE_GROUP_ITEM);
}
}
if (received) {
if (item_processed) {
XdrvMailbox.command_code = DGR_ITEM_EOL;
XdrvCall(FUNC_DEVICE_GROUP_ITEM);
}
skip_light_fade = false;
ignore_dgr_sends = false;
}
write_log:
AddLog_P(LOG_LEVEL_DEBUG_MORE, log_buffer);
// If this is a received status request message, then if the requestor didn't just ack our
// previous full status update, send a full status update.
if (received) {
if ((flags & DGR_FLAG_STATUS_REQUEST)) {
if ((flags & DGR_FLAG_RESET) || device_group_member->acked_sequence != device_group->last_full_status_sequence) {
_SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS);
}
}
}
// If this is a message being sent, send it.
else {
for (int attempt = 1; attempt <= 5; attempt++) {
if (PortUdp.beginPacket((device_group_member ? device_group_member->ip_address : IPAddress(239,255,255,250)), 1900)) {
PortUdp_write(message, message_length);
if (PortUdp.endPacket()) return;
}
delay(10);
}
AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error sending message"));
}
goto cleanup;
badmsg:
AddLog_P(LOG_LEVEL_ERROR, PSTR("%s ** incorrect length"), log_buffer);
cleanup:
if (received) {
skip_light_fade = false;
ignore_dgr_sends = false;
} }
AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error sending %s packet"), label);
} }
void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...) void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...)
@ -184,7 +419,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
if (!udp_connected) return; if (!udp_connected) return;
// If we're currently processing a remote device message, ignore this request. // If we're currently processing a remote device message, ignore this request.
if (processing_remote_device_message && message_type != DGR_MSGTYPE_UPDATE_COMMAND) return; if (ignore_dgr_sends && message_type != DGR_MSGTYPE_UPDATE_COMMAND) return;
// Get a pointer to the device information for this device. // Get a pointer to the device information for this device.
struct device_group * device_group = &device_groups[device_group_index]; struct device_group * device_group = &device_groups[device_group_index];
@ -192,35 +427,44 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
// If we're still sending initial status requests, ignore this request. // If we're still sending initial status requests, ignore this request.
if (device_group->initial_status_requests_remaining) return; if (device_group->initial_status_requests_remaining) return;
// Load the message header, sequence and flags.
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Building %s %spacket"), device_group->group_name, (message_type == DGR_MSGTYP_FULL_STATUS ? PSTR("full status ") : PSTR("")));
#endif // DEVICE_GROUPS_DEBUG
#ifdef USE_DEVICE_GROUPS_SEND
#endif // USE_DEVICE_GROUPS_SEND
uint16_t original_sequence = device_group->outgoing_sequence;
uint16_t flags = 0;
if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME)
flags = DGR_FLAG_MORE_TO_COME;
else if (message_type == DGR_MSGTYP_UPDATE_DIRECT)
flags = DGR_FLAG_DIRECT;
char * message_ptr = BeginDeviceGroupMessage(device_group, flags, building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE);
// A full status request is a request from a remote device for the status of every item we // A full status request is a request from a remote device for the status of every item we
// control. As long as we're building it, we may as well multicast the status update to all // control. As long as we're building it, we may as well multicast the status update to all
// device group members. // device group members.
char * message_ptr = &device_group->message[device_group->message_header_length];
if (message_type == DGR_MSGTYP_FULL_STATUS) { if (message_type == DGR_MSGTYP_FULL_STATUS) {
device_group->last_full_status_sequence = device_group->outgoing_sequence;
device_group->message_length = 0;
// Set the flag indicating we're currently building a status message. SendDeviceGroupMessage // Set the flag indicating we're currently building a status message. SendDeviceGroupMessage
// will build but not send messages while this flag is set. // will build but not send messages while this flag is set.
building_status_message = true; building_status_message = true;
// Call the drivers to build the status update. // Call the drivers to build the status update.
if (!++outgoing_sequence) outgoing_sequence = 1; if (device_group->local) SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power);
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Building device group %s full status packet"), device_group->group_name);
#endif // DEVICE_GROUPS_DEBUG
device_group->message_length = 0;
SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power);
XdrvMailbox.index = device_group_index << 16; XdrvMailbox.index = device_group_index << 16;
XdrvMailbox.command_code = DGR_ITEM_STATUS; XdrvMailbox.command_code = DGR_ITEM_STATUS;
XdrvMailbox.topic = (char *)&device_group_index; XdrvMailbox.topic = (char *)&device_group_index;
XdrvCall(FUNC_DEVICE_GROUP_ITEM); XdrvCall(FUNC_DEVICE_GROUP_ITEM);
building_status_message = false; building_status_message = false;
// If we have nothing to share with the other members, restore the message sequence and return. // If we have nothing to share with the other members, just send the EOL item.
if (!device_group->message_length) { if (!device_group->message_length) {
if (!--outgoing_sequence) outgoing_sequence = -1; *message_ptr++ = 0;
return; device_group->message_length = message_ptr - device_group->message;
} }
device_group->last_full_status_sequence = outgoing_sequence;
// Set the status update flag in the outgoing message. // Set the status update flag in the outgoing message.
*(message_ptr + 2) |= DGR_FLAG_FULL_STATUS; *(message_ptr + 2) |= DGR_FLAG_FULL_STATUS;
@ -229,7 +473,6 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
else { else {
#ifdef USE_DEVICE_GROUPS_SEND #ifdef USE_DEVICE_GROUPS_SEND
bool escaped; bool escaped;
bool use_command;
char chr; char chr;
char oper; char oper;
uint32_t old_value; uint32_t old_value;
@ -240,6 +483,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
uint32_t value; uint32_t value;
void * value_ptr; void * value_ptr;
} item_array[32]; } item_array[32];
// bool items_added = false;
bool shared; bool shared;
uint8_t item; uint8_t item;
uint32_t value; uint32_t value;
@ -247,26 +491,11 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
struct item * item_ptr; struct item * item_ptr;
va_list ap; va_list ap;
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Building device group %s packet"), device_group->group_name);
#endif // DEVICE_GROUPS_DEBUG
value = 0;
if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME)
value |= DGR_FLAG_MORE_TO_COME;
else if (message_type == DGR_MSGTYP_UPDATE_DIRECT)
value |= DGR_FLAG_DIRECT;
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">sequence=%u, flags=%u"), outgoing_sequence, value);
#endif // DEVICE_GROUPS_DEBUG
message_ptr = BeginDeviceGroupMessage(device_group, value, building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE);
// Build an array of all the items and values in this update. // Build an array of all the items and values in this update.
memset(item_array, 0, sizeof(item_array)); memset(item_array, 0, sizeof(item_array));
item_ptr = item_array; item_ptr = item_array;
#ifdef USE_DEVICE_GROUPS_SEND #ifdef USE_DEVICE_GROUPS_SEND
use_command = (message_type == DGR_MSGTYPE_UPDATE_COMMAND); if (message_type == DGR_MSGTYPE_UPDATE_COMMAND) {
if (use_command) {
value_ptr = XdrvMailbox.data; value_ptr = XdrvMailbox.data;
while ((item = strtoul(value_ptr, &value_ptr, 0))) { while ((item = strtoul(value_ptr, &value_ptr, 0))) {
item_ptr->item = item; item_ptr->item = item;
@ -405,7 +634,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
} }
} }
#ifdef DEVICE_GROUPS_DEBUG #ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%u items carried over from previous update"), kept_item_count); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: %u items carried over"), kept_item_count);
#endif // DEVICE_GROUPS_DEBUG #endif // DEVICE_GROUPS_DEBUG
} }
@ -416,52 +645,28 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
shared = true; shared = true;
if (!device_group_index) shared = DeviceGroupItemShared(false, item); if (!device_group_index) shared = DeviceGroupItemShared(false, item);
if (shared) { if (shared) {
// items_added = true;
*message_ptr++ = item; *message_ptr++ = item;
// For integer items, add the value to the message. // For integer items, add the value to the message.
if (item <= DGR_ITEM_MAX_32BIT) { if (item <= DGR_ITEM_MAX_32BIT) {
value = item_ptr->value; value = item_ptr->value;
//#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(">%u=%u"), item, value);
//#endif // DEVICE_GROUPS_DEBUG
*message_ptr++ = value & 0xff; *message_ptr++ = value & 0xff;
if (item > DGR_ITEM_MAX_8BIT) { if (item > DGR_ITEM_MAX_8BIT) {
#ifdef USE_DEVICE_GROUPS_SEND
old_value = value;
#endif // USE_DEVICE_GROUPS_SEND
value >>= 8; value >>= 8;
*message_ptr++ = value & 0xff; *message_ptr++ = value & 0xff;
if (item > DGR_ITEM_MAX_16BIT) { if (item > DGR_ITEM_MAX_16BIT) {
value >>= 8; value >>= 8;
*message_ptr++ = value & 0xff; *message_ptr++ = value & 0xff;
value >>= 8; value >>= 8;
// For the power item, the device count is overlayed onto the highest 8 bits. // For the power item, the device count is overlayed onto the highest 8 bits.
if (item == DGR_ITEM_POWER) { if (item == DGR_ITEM_POWER) {
if (!value) if (!value)
value = (device_group_index == 0 ? devices_present : 1); value = (device_group_index == 0 ? devices_present : 1);
#ifdef USE_DEVICE_GROUPS_SEND
else
old_value = old_value & 0xffffff;
#endif // USE_DEVICE_GROUPS_SEND
} }
*message_ptr++ = value; *message_ptr++ = value;
#ifdef USE_DEVICE_GROUPS_SEND
device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = old_value;
#endif // USE_DEVICE_GROUPS_SEND
} }
#ifdef USE_DEVICE_GROUPS_SEND
else {
device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = old_value;
}
#endif // USE_DEVICE_GROUPS_SEND
} }
#ifdef USE_DEVICE_GROUPS_SEND
else {
device_group->values_8bit[item] = value;
}
#endif // USE_DEVICE_GROUPS_SEND
} }
// For string items, add the null-terminated string to the message. // For string items, add the null-terminated string to the message.
@ -472,9 +677,6 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
message_ptr += value; message_ptr += value;
} }
*message_ptr++ = 0; *message_ptr++ = 0;
//#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(">%u='%s'"), item, item_ptr->value_ptr);
//#endif // DEVICE_GROUPS_DEBUG
} }
// For special items, handle them individually. // For special items, handle them individually.
@ -485,28 +687,39 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
*message_ptr++ = (value_ptr ? *value_ptr++ : 0); *message_ptr++ = (value_ptr ? *value_ptr++ : 0);
} }
//#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(">%u=%u,%u,%u,%u,%u"), item, *(message_ptr - 5), *(message_ptr - 4), *(message_ptr - 3), *(message_ptr - 2), *(message_ptr - 1));
//#endif // DEVICE_GROUPS_DEBUG
break; break;
} }
} }
} }
} }
// Add the EOL item code and calculate the message length. // If we added any items, add the EOL item code and calculate the message length.
*message_ptr++ = 0; // if (items_added) {
device_group->message_length = message_ptr - device_group->message; *message_ptr++ = 0;
device_group->message_length = message_ptr - device_group->message;
// }
// If there's going to be more items added to this message, return. // If there's going to be more items added to this message, return.
if (building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE) return; if (building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE) return;
} }
// If there is no message, restore the sequence number and return.
if (!device_group->message_length) {
device_group->outgoing_sequence = original_sequence;
return;
}
// Multicast the packet. // Multicast the packet.
#ifdef DEVICE_GROUPS_DEBUG SendReceiveDeviceGroupPacket(device_group, nullptr, device_group->message, device_group->message_length, false);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending %u-byte device group %s packet via multicast, sequence=%u"), device_group->message_length, device_group->group_name, device_group->message[device_group->message_header_length] | device_group->message[device_group->message_header_length + 1] << 8);
#endif // DEVICE_GROUPS_DEBUG #ifdef USE_DEVICE_GROUPS_SEND
SendDeviceGroupPacket(IPAddress(0,0,0,0), device_group->message, device_group->message_length, PSTR("Multicast")); // If the device group is local, handle the message locally.
if (message_type == DGR_MSGTYPE_UPDATE_COMMAND && device_group->local) {
struct XDRVMAILBOX save_XdrvMailbox = XdrvMailbox;
SendReceiveDeviceGroupPacket(device_group, nullptr, device_group->message, device_group->message_length, true);
XdrvMailbox = save_XdrvMailbox;
}
#endif // USE_DEVICE_GROUPS_SEND
uint32_t now = millis(); uint32_t now = millis();
if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) { if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) {
@ -514,7 +727,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
device_group->next_ack_check_time = 0; device_group->next_ack_check_time = 0;
} }
else { else {
device_group->ack_check_interval = 100; device_group->ack_check_interval = 200;
device_group->next_ack_check_time = now + device_group->ack_check_interval; device_group->next_ack_check_time = now + device_group->ack_check_interval;
if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time;
device_group->member_timeout_time = now + DGR_MEMBER_TIMEOUT; device_group->member_timeout_time = now + DGR_MEMBER_TIMEOUT;
@ -524,10 +737,10 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time;
} }
void ProcessDeviceGroupMessage(char * packet, int packet_length) void ProcessDeviceGroupMessage(char * message, int message_length)
{ {
// Make the group name a null-terminated string. // Make the group name a null-terminated string.
char * message_group_name = packet + sizeof(DEVICE_GROUP_MESSAGE) - 1; char * message_group_name = message + sizeof(DEVICE_GROUP_MESSAGE) - 1;
char * message_ptr = strchr(message_group_name, ' '); char * message_ptr = strchr(message_group_name, ' ');
if (message_ptr == nullptr) return; if (message_ptr == nullptr) return;
*message_ptr = 0; *message_ptr = 0;
@ -543,15 +756,15 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length)
*message_ptr++ = ' '; *message_ptr++ = ' ';
// Find the group member. If this is a new group member, add it. // Find the group member. If this is a new group member, add it.
struct device_group_member * device_group_member;
IPAddress remote_ip = PortUdp.remoteIP(); IPAddress remote_ip = PortUdp.remoteIP();
struct device_group_member * * flink = &device_group->device_group_members; struct device_group_member * * flink = &device_group->device_group_members;
struct device_group_member * device_group_member;
for (;;) { for (;;) {
device_group_member = *flink; device_group_member = *flink;
if (!device_group_member) { if (!device_group_member) {
device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member)); device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member));
if (device_group_member == nullptr) { if (device_group_member == nullptr) {
AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating device group member block")); AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating member block"));
return; return;
} }
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Adding member %s"), IPAddressToString(remote_ip)); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Adding member %s"), IPAddressToString(remote_ip));
@ -565,205 +778,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length)
flink = &device_group_member->flink; flink = &device_group_member->flink;
} }
// Find the start of the actual message (after the http header). SendReceiveDeviceGroupPacket(device_group, device_group_member, message, message_length, true);
message_ptr = strstr_P(message_ptr, PSTR("\n\n"));
if (message_ptr == nullptr) return;
message_ptr += 2;
bool light_fade;
uint16_t flags;
uint16_t item;
uint16_t message_sequence;
int32_t value;
// Get the message sequence and flags.
if (packet_length - (message_ptr - packet) < 4) goto badmsg; // Malformed message - must be at least 16-bit sequence, 16-bit flags left
message_sequence = *message_ptr++;
message_sequence |= *message_ptr++ << 8;
flags = *message_ptr++;
flags |= *message_ptr++ << 8;
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Received device group %s packet from %s: sequence=%u, flags=%u"), device_group->group_name, IPAddressToString(remote_ip), message_sequence, flags);
#endif // DEVICE_GROUPS_DEBUG
// If this is an announcement, simply return.
if (flags == DGR_FLAG_ANNOUNCEMENT) return;
// If this is an ack message, save the message sequence if it's newwer than the last ack we
// received from this member.
if (flags == DGR_FLAG_ACK) {
if (message_sequence > device_group_member->acked_sequence || device_group_member->acked_sequence - message_sequence < 64536) {
device_group_member->acked_sequence = message_sequence;
}
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("<ack"));
#endif // DEVICE_GROUPS_DEBUG
return;
}
// Send an ack message to the sender.
if (!(flags & DGR_FLAG_MORE_TO_COME)) {
*(message_ptr - 2) = DGR_FLAG_ACK;
*(message_ptr - 1) = 0;
SendDeviceGroupPacket(remote_ip, packet, message_ptr - packet, PSTR("Ack"));
}
// If this is a status request message, then if the requestor didn't just ack our previous full
// status update, send a full status update.
if ((flags & DGR_FLAG_STATUS_REQUEST)) {
if ((flags & DGR_FLAG_RESET) || device_group_member->acked_sequence != device_group->last_full_status_sequence) {
_SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_FULL_STATUS);
}
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("<status request"));
#endif // DEVICE_GROUPS_DEBUG
return;
}
// If we already processed this or a later message from this group member, ignore this message.
if (message_sequence <= device_group_member->received_sequence) {
if (message_sequence == device_group_member->received_sequence || device_group_member->received_sequence - message_sequence > 64536) {
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("<old message"));
#endif // DEVICE_GROUPS_DEBUG
return;
}
}
device_group_member->received_sequence = message_sequence;
// Set the flag indicating we're currently processing a remote device message.
// SendDeviceGroupMessage will not send messages while this flag is set.
processing_remote_device_message = true;
/*
XdrvMailbox
bool grpflg
bool usridx
uint16_t command_code Item code
uint32_t index 0:15 Flags, 16:31 Message sequence
uint32_t data_len String item value length
int32_t payload Integer item value
char *topic Pointer to device group index
char *data Pointer to non-integer item value
char *command nullptr
*/
XdrvMailbox.command = nullptr; // Indicates the source is a device group update
XdrvMailbox.index = flags | message_sequence << 16;
XdrvMailbox.topic = (char *)&device_group_index;
if (flags & (DGR_FLAG_MORE_TO_COME | DGR_FLAG_DIRECT)) skip_light_fade = true;
for (;;) {
if (packet_length - (message_ptr - packet) < 1) goto badmsg; // Malformed message
item = *message_ptr++;
if (!item) break; // Done
#ifdef DEVICE_GROUPS_DEBUG
switch (item) {
case DGR_ITEM_LIGHT_FADE:
case DGR_ITEM_LIGHT_SPEED:
case DGR_ITEM_LIGHT_BRI:
case DGR_ITEM_LIGHT_SCHEME:
case DGR_ITEM_LIGHT_FIXED_COLOR:
case DGR_ITEM_BRI_PRESET_LOW:
case DGR_ITEM_BRI_PRESET_HIGH:
case DGR_ITEM_BRI_POWER_ON:
case DGR_ITEM_POWER:
case DGR_ITEM_EVENT:
case DGR_ITEM_LIGHT_CHANNELS:
break;
default:
AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: ********** Invalid item=%u received from device group %s member %s"), item, device_group->group_name, IPAddressToString(remote_ip));
}
#endif // DEVICE_GROUPS_DEBUG
XdrvMailbox.command_code = item;
if (item <= DGR_ITEM_LAST_32BIT) {
value = *message_ptr++;
if (item > DGR_ITEM_MAX_8BIT) {
value |= *message_ptr++ << 8;
if (item > DGR_ITEM_MAX_16BIT) {
value |= *message_ptr++ << 16;
value |= *message_ptr++ << 24;
#ifdef USE_DEVICE_GROUPS_SEND
device_group->values_32bit[item - DGR_ITEM_MAX_16BIT - 1] = (item == DGR_ITEM_POWER ? value & 0xffffff : value);
#endif // USE_DEVICE_GROUPS_SEND
}
#ifdef USE_DEVICE_GROUPS_SEND
else {
device_group->values_16bit[item - DGR_ITEM_MAX_8BIT - 1] = value;
}
#endif // USE_DEVICE_GROUPS_SEND
}
#ifdef USE_DEVICE_GROUPS_SEND
else {
device_group->values_8bit[item] = value;
}
#endif // USE_DEVICE_GROUPS_SEND
//#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("<%u=%u"), item, value);
//#endif // DEVICE_GROUPS_DEBUG
XdrvMailbox.payload = value;
}
else if (item <= DGR_ITEM_MAX_STRING) {
value = strlen(message_ptr);
if (value >= packet_length - (message_ptr - packet)) goto badmsg; // Malformed message
//#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("<%u='%s'"), item, message_ptr);
//#endif // DEVICE_GROUPS_DEBUG
XdrvMailbox.data_len = value;
XdrvMailbox.data = message_ptr;
message_ptr += value + 1;
}
else {
switch (item) {
case DGR_ITEM_LIGHT_CHANNELS:
//#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("<%u=%u,%u,%u,%u,%u"), item, *message_ptr, *(message_ptr + 1), *(message_ptr + 2), *(message_ptr + 3), *(message_ptr + 4));
//#endif // DEVICE_GROUPS_DEBUG
XdrvMailbox.data = message_ptr;
message_ptr += 5;
break;
}
}
if (DeviceGroupItemShared(true, item)) {
if (item == DGR_ITEM_POWER) {
if (device_group->local) {
uint8_t mask_devices = value >> 24;
if (mask_devices > devices_present) mask_devices = devices_present;
for (uint32_t i = 0; i < devices_present; i++) {
uint32_t mask = 1 << i;
bool on = (value & mask);
if (on != (power & mask)) ExecuteCommandPower(i + 1, (on ? POWER_ON : POWER_OFF), SRC_REMOTE);
}
}
}
else if (item == DGR_ITEM_EVENT) {
#ifdef USE_RULES
CmndEvent();
#endif
}
XdrvCall(FUNC_DEVICE_GROUP_ITEM);
}
}
XdrvMailbox.command_code = DGR_ITEM_EOL;
XdrvCall(FUNC_DEVICE_GROUP_ITEM);
skip_light_fade = false;
processing_remote_device_message = false;
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("<processed"));
#endif // DEVICE_GROUPS_DEBUG
return;
badmsg:
AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: Malformed message received from %s"), IPAddressToString(remote_ip));
#ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("packet_length=%u, offset=%u"), packet_length, message_ptr - packet);
#endif // DEVICE_GROUPS_DEBUG
processing_remote_device_message = false;
} }
void DeviceGroupStatus(uint8_t device_group_index) void DeviceGroupStatus(uint8_t device_group_index)
@ -774,10 +789,10 @@ void DeviceGroupStatus(uint8_t device_group_index)
struct device_group * device_group = &device_groups[device_group_index]; struct device_group * device_group = &device_groups[device_group_index];
buffer[0] = buffer[1] = 0; buffer[0] = buffer[1] = 0;
for (struct device_group_member * device_group_member = device_group->device_group_members; device_group_member; device_group_member = device_group_member->flink) { for (struct device_group_member * device_group_member = device_group->device_group_members; device_group_member; device_group_member = device_group_member->flink) {
snprintf(buffer, sizeof(buffer), PSTR("%s,{\"IPAddress\":\"%s\",\"ResendCount\":%u,\"LastRcvdSeq\":%u,\"LastAckedSeq\":%u}"), buffer, IPAddressToString(device_group_member->ip_address), device_group_member->unicast_count, device_group_member->received_sequence, device_group_member->acked_sequence); snprintf_P(buffer, sizeof(buffer), PSTR("%s,{\"IPAddress\":\"%s\",\"ResendCount\":%u,\"LastRcvdSeq\":%u,\"LastAckedSeq\":%u}"), buffer, IPAddressToString(device_group_member->ip_address), device_group_member->unicast_count, device_group_member->received_sequence, device_group_member->acked_sequence);
member_count++; member_count++;
} }
Response_P(PSTR("{\"" D_CMND_DEVGROUPSTATUS "\":{\"Index\":%u,\"GroupName\":\"%s\",\"MessageSeq\":%u,\"MemberCount\":%d,\"Members\":[%s]}"), device_group_index, device_group->group_name, outgoing_sequence, member_count, &buffer[1]); Response_P(PSTR("{\"" D_CMND_DEVGROUPSTATUS "\":{\"Index\":%u,\"GroupName\":\"%s\",\"MessageSeq\":%u,\"MemberCount\":%d,\"Members\":[%s]}"), device_group_index, device_group->group_name, device_group->outgoing_sequence, member_count, &buffer[1]);
} }
} }
@ -799,11 +814,12 @@ void DeviceGroupsLoop(void)
next_check_time = now + 3000; next_check_time = now + 3000;
for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) { for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) {
device_group * device_group = &device_groups[device_group_index]; device_group * device_group = &device_groups[device_group_index];
device_group->next_announcement_time = -1;
device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_RESET | DGR_FLAG_STATUS_REQUEST) - device_group->message; device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_RESET | DGR_FLAG_STATUS_REQUEST) - device_group->message;
device_group->initial_status_requests_remaining = 10; device_group->initial_status_requests_remaining = 10;
device_group->next_ack_check_time = next_check_time; device_group->next_ack_check_time = next_check_time;
} }
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: (Re)discovering device groups")); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: (Re)discovering members"));
} }
if (device_groups_initialization_failed) return; if (device_groups_initialization_failed) return;
@ -830,7 +846,8 @@ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Ckecking next_check_time=%u, now=%u"), nex
#ifdef DEVICE_GROUPS_DEBUG #ifdef DEVICE_GROUPS_DEBUG
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending initial status request for group %s"), device_group->group_name); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending initial status request for group %s"), device_group->group_name);
#endif // DEVICE_GROUPS_DEBUG #endif // DEVICE_GROUPS_DEBUG
SendDeviceGroupPacket(IPAddress(0,0,0,0), device_group->message, device_group->message_length, PSTR("Initial")); SendReceiveDeviceGroupPacket(device_group, nullptr, device_group->message, device_group->message_length, false);
device_group->message[device_group->message_header_length + 2] = DGR_FLAG_STATUS_REQUEST; // The reset flag is on only for the first packet - turn it off now device_group->message[device_group->message_header_length + 2] = DGR_FLAG_STATUS_REQUEST; // The reset flag is on only for the first packet - turn it off now
device_group->next_ack_check_time = now + 200; device_group->next_ack_check_time = now + 200;
} }
@ -854,7 +871,7 @@ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Ckecking next_check_time=%u, now=%u"), nex
while ((device_group_member = *flink)) { while ((device_group_member = *flink)) {
// If we have not received an ack to our last message from this member, ... // If we have not received an ack to our last message from this member, ...
if (device_group_member->acked_sequence != outgoing_sequence) { if (device_group_member->acked_sequence != device_group->outgoing_sequence) {
// If we haven't receive an ack from this member in DGR_MEMBER_TIMEOUT ms, assume // If we haven't receive an ack from this member in DGR_MEMBER_TIMEOUT ms, assume
// they're offline and remove them from the group. // they're offline and remove them from the group.
@ -866,10 +883,7 @@ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Ckecking next_check_time=%u, now=%u"), nex
// Otherwise, unicast the last message directly to this member. // Otherwise, unicast the last message directly to this member.
else { else {
#ifdef DEVICE_GROUPS_DEBUG SendReceiveDeviceGroupPacket(device_group, device_group_member, device_group->message, device_group->message_length, false);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending %u-byte device group %s packet via unicast to %s, sequence %u, last message acked=%u"), device_group->message_length, device_group->group_name, IPAddressToString(device_group_member->ip_address), outgoing_sequence, device_group_member->acked_sequence);
#endif // DEVICE_GROUPS_DEBUG
SendDeviceGroupPacket(device_group_member->ip_address, device_group->message, device_group->message_length, PSTR("Unicast"));
device_group_member->unicast_count++; device_group_member->unicast_count++;
acked = false; acked = false;
flink = &device_group_member->flink; flink = &device_group_member->flink;
@ -910,10 +924,7 @@ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Ckecking next_check_time=%u, now=%u"), nex
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now);
#endif // DEVICE_GROUPS_DEBUG #endif // DEVICE_GROUPS_DEBUG
if (device_group->next_announcement_time <= now) { if (device_group->next_announcement_time <= now) {
#ifdef DEVICE_GROUPS_DEBUG SendReceiveDeviceGroupPacket(device_group, nullptr, device_group->message, BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT, true) - device_group->message, false);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Sending device group %s announcement"), device_group->group_name);
#endif // DEVICE_GROUPS_DEBUG
SendDeviceGroupPacket(IPAddress(0,0,0,0), device_group->message, BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT, true) - device_group->message, PSTR("Announcement"));
device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL + random(10000); device_group->next_announcement_time = now + DGR_ANNOUNCEMENT_INTERVAL + random(10000);
} }
if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time;

View File

@ -315,7 +315,7 @@ enum DevGroupItem { DGR_ITEM_EOL, DGR_ITEM_STATUS,
DGR_ITEM_POWER, DGR_ITEM_DIMMER_RANGE, DGR_ITEM_POWER, DGR_ITEM_DIMMER_RANGE,
// Add new 32-bit items before this line // Add new 32-bit items before this line
DGR_ITEM_LAST_32BIT, DGR_ITEM_MAX_32BIT = 191, DGR_ITEM_LAST_32BIT, DGR_ITEM_MAX_32BIT = 191,
DGR_ITEM_EVENT, DGR_ITEM_EVENT, DGR_ITEM_COMMAND,
// Add new string items before this line // Add new string items before this line
DGR_ITEM_LAST_STRING, DGR_ITEM_MAX_STRING = 223, DGR_ITEM_LAST_STRING, DGR_ITEM_MAX_STRING = 223,
DGR_ITEM_LIGHT_CHANNELS }; DGR_ITEM_LIGHT_CHANNELS };

View File

@ -282,6 +282,7 @@ struct LIGHT {
bool fade_initialized = false; // dont't fade at startup bool fade_initialized = false; // dont't fade at startup
bool fade_running = false; bool fade_running = false;
#ifdef USE_DEVICE_GROUPS #ifdef USE_DEVICE_GROUPS
uint8_t last_scheme = 0;
bool devgrp_no_channels_out = false; // don't share channels with device group (e.g. if scheme set by other device) bool devgrp_no_channels_out = false; // don't share channels with device group (e.g. if scheme set by other device)
#endif // USE_DEVICE_GROUPS #endif // USE_DEVICE_GROUPS
#ifdef USE_LIGHT_PALETTE #ifdef USE_LIGHT_PALETTE
@ -1477,9 +1478,6 @@ void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value)
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal); // AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal);
light_controller.changeRGB(signal, 255 - signal, 0, true); // keep bri light_controller.changeRGB(signal, 255 - signal, 0, true); // keep bri
Settings.light_scheme = 0; Settings.light_scheme = 0;
#ifdef USE_DEVICE_GROUPS
LightUpdateScheme();
#endif // USE_DEVICE_GROUPS
if (0 == light_state.getBri()) { if (0 == light_state.getBri()) {
light_controller.changeBri(50); light_controller.changeBri(50);
} }
@ -1833,9 +1831,6 @@ void LightAnimate(void)
Light.wakeup_active = 0; Light.wakeup_active = 0;
Settings.light_scheme = LS_POWER; Settings.light_scheme = LS_POWER;
#ifdef USE_DEVICE_GROUPS
LightUpdateScheme();
#endif // USE_DEVICE_GROUPS
} }
} }
break; break;
@ -1858,6 +1853,14 @@ void LightAnimate(void)
default: default:
XlgtCall(FUNC_SET_SCHEME); XlgtCall(FUNC_SET_SCHEME);
} }
#ifdef USE_DEVICE_GROUPS
if (Settings.light_scheme != Light.last_scheme) {
Light.last_scheme = Settings.light_scheme;
SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme);
Light.devgrp_no_channels_out = false;
}
#endif // USE_DEVICE_GROUPS
} }
if ((Settings.light_scheme < LS_MAX) || power_off) { // exclude WS281X Neopixel schemes if ((Settings.light_scheme < LS_MAX) || power_off) { // exclude WS281X Neopixel schemes
@ -2270,7 +2273,7 @@ void LightHandleDevGroupItem(void)
break; break;
case DGR_ITEM_LIGHT_SCHEME: case DGR_ITEM_LIGHT_SCHEME:
if (Settings.light_scheme != value) { if (Settings.light_scheme != value) {
Settings.light_scheme = value; Light.last_scheme = Settings.light_scheme = value;
Light.devgrp_no_channels_out = (value != 0); Light.devgrp_no_channels_out = (value != 0);
send_state = true; send_state = true;
} }
@ -2330,17 +2333,6 @@ void LightHandleDevGroupItem(void)
break; break;
} }
} }
void LightUpdateScheme(void)
{
static uint8_t last_scheme;
if (Settings.light_scheme != last_scheme) {
last_scheme = Settings.light_scheme;
SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_SCHEME, Settings.light_scheme);
}
Light.devgrp_no_channels_out = false;
}
#endif // USE_DEVICE_GROUPS #endif // USE_DEVICE_GROUPS
/*********************************************************************************************\ /*********************************************************************************************\
@ -2473,9 +2465,6 @@ void CmndSupportColor(void)
} }
#endif // USE_LIGHT_PALETTE #endif // USE_LIGHT_PALETTE
Settings.light_scheme = 0; Settings.light_scheme = 0;
#ifdef USE_DEVICE_GROUPS
LightUpdateScheme();
#endif // USE_DEVICE_GROUPS
coldim = true; coldim = true;
} else { // Color3, 4, 5 and 6 } else { // Color3, 4, 5 and 6
for (uint32_t i = 0; i < LST_RGB; i++) { for (uint32_t i = 0; i < LST_RGB; i++) {
@ -2645,9 +2634,6 @@ void CmndScheme(void)
#endif // USE_LIGHT_PALETTE #endif // USE_LIGHT_PALETTE
} }
Settings.light_scheme = XdrvMailbox.payload; Settings.light_scheme = XdrvMailbox.payload;
#ifdef USE_DEVICE_GROUPS
LightUpdateScheme();
#endif // USE_DEVICE_GROUPS
if (LS_WAKEUP == Settings.light_scheme) { if (LS_WAKEUP == Settings.light_scheme) {
Light.wakeup_active = 3; Light.wakeup_active = 3;
} }
@ -2671,9 +2657,6 @@ void CmndWakeup(void)
} }
Light.wakeup_active = 3; Light.wakeup_active = 3;
Settings.light_scheme = LS_WAKEUP; Settings.light_scheme = LS_WAKEUP;
#ifdef USE_DEVICE_GROUPS
LightUpdateScheme();
#endif // USE_DEVICE_GROUPS
LightPowerOn(); LightPowerOn();
ResponseCmndChar(D_JSON_STARTED); ResponseCmndChar(D_JSON_STARTED);
} }
@ -2753,8 +2736,11 @@ void CmndDimmer(void)
} }
} }
#if defined(USE_PWM_DIMMER) && defined(USE_DEVICE_GROUPS) #if defined(USE_PWM_DIMMER) && defined(USE_DEVICE_GROUPS)
Settings.bri_power_on = light_state.getBri();; uint8_t bri = light_state.getBri();
SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on); if (bri != Settings.bri_power_on) {
Settings.bri_power_on = bri;
SendLocalDeviceGroupMessage(DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on);
}
#endif // USE_PWM_DIMMER && USE_DEVICE_GROUPS #endif // USE_PWM_DIMMER && USE_DEVICE_GROUPS
Light.update = true; Light.update = true;
if (skip_light_fade) LightAnimate(); if (skip_light_fade) LightAnimate();

View File

@ -344,8 +344,8 @@ void PWMDimmerHandleButton(void)
// If this is about the power button, ... // If this is about the power button, ...
if (is_power_button) { if (is_power_button) {
// If no other buttons are pressed and the up or down button was tapped while holding the // If no other buttons are pressed and the up or down button was not tapped while holding
// power button before this, ... // the power button before this, ...
if (buttons_pressed == 1 && !tap_count) { if (buttons_pressed == 1 && !tap_count) {
// If the power is on, adjust the brightness. Set the direction based on the current // If the power is on, adjust the brightness. Set the direction based on the current
@ -602,7 +602,7 @@ void PWMDimmerHandleButton(void)
} }
if (new_bri != bri) { if (new_bri != bri) {
#ifdef USE_DEVICE_GROUPS #ifdef USE_DEVICE_GROUPS
SendDeviceGroupMessage(power_button_index, (dgr_item ? DGR_MSGTYP_UPDATE : DGR_MSGTYP_UPDATE_MORE_TO_COME), DGR_ITEM_LIGHT_BRI, new_bri); SendDeviceGroupMessage(power_button_index, (dgr_item ? DGR_MSGTYP_PARTIAL_UPDATE : DGR_MSGTYP_UPDATE_MORE_TO_COME), DGR_ITEM_LIGHT_BRI, new_bri);
#endif // USE_DEVICE_GROUPS #endif // USE_DEVICE_GROUPS
#ifdef USE_PWM_DIMMER_REMOTE #ifdef USE_PWM_DIMMER_REMOTE
if (!active_device_is_local) if (!active_device_is_local)
@ -610,9 +610,15 @@ void PWMDimmerHandleButton(void)
else { else {
#endif // USE_PWM_DIMMER_REMOTE #endif // USE_PWM_DIMMER_REMOTE
skip_light_fade = true; skip_light_fade = true;
#ifdef USE_DEVICE_GROUPS
ignore_dgr_sends = true;
#endif // USE_DEVICE_GROUPS
light_state.setBri(new_bri); light_state.setBri(new_bri);
LightAnimate(); LightAnimate();
skip_light_fade = false; skip_light_fade = false;
#ifdef USE_DEVICE_GROUPS
ignore_dgr_sends = false;
#endif // USE_DEVICE_GROUPS
Settings.bri_power_on = new_bri; Settings.bri_power_on = new_bri;
#ifdef USE_PWM_DIMMER_REMOTE #ifdef USE_PWM_DIMMER_REMOTE
} }