diff --git a/Device_Groups.md b/Device_Groups.md
index c5990136f..3384132b4 100644
--- a/Device_Groups.md
+++ b/Device_Groups.md
@@ -2,7 +2,7 @@
The device groups module provides a framework to allow multiple devices to be in a group with values such as power, light color/temperature/brightness, PWM values, sensor values, etc. shared with other devices in the group. For example, with multiple light modules in a device group, the light settings can be changed on one module and the settings will automatically be changed on the other light modules. Dimmer switch modules could be in a device group with light modules and the dimmer switch could control the power, brightness and colors of all the lights in the group. Multiple dimmer switches could be in a device group to form a 3-way/4-way dimmer switch.
-UDP broadcasts, followed by UDP unicasts if necessary, are used to send updates to all devices so updates are fast. There is no need for an MQTT server but all the devices in a group must be on the same IP network.
+UDP multicasts, followed by UDP unicasts if necessary, are used to send updates to all devices so updates are fast. There is no need for an MQTT server but all the devices in a group must be on the same IP network.
To include device groups support in the build, define USE_DEVICE_GROUPS in your user_config_override. This adds 3.5K to the code size. All devices in a group must be running firmware with device group support and have device groups enabled.
@@ -13,7 +13,7 @@ To enable device groups, set Option16 to 1 and **restart the device**.
The device group name is the MQTT group topic set with the GroupTopic command. All devices in the same IP network with the same group topic are in the same group. Some modules may define additional device groups. For example, if Remote Device Mode is enabled, the PWM Dimmer module defines three devices groups.
-The items that are sent to the group and the items that are received from the group are selected with the DevGroupShare command. By default all items are sent and received from the group. An example of when the DevGroupShare command would be used is when you have a group of lights that you control with a dimmer switch and home automation software. You want the dimmer switch to be able to control all items. The home automation software controls each light individually. When it controls the whole group, it actually sends command to each light in the group. If you use the home automation software to turn an individual light on or off or change it’s brightness, color or scheme, you do not want the change to be replicated to the other lights. In this case, you would set the incoming and outgoing item masks to 255 on the dimmer switch (DevGroupShare 255,255) and set the incoming item mask to 255 and outgoing item mask to 0 on all the lights (DevGroupShare 255,0).
+The items that are sent to the group and the items that are received from the group are selected with the DevGroupShare command. By default all items are sent and received from the group. An example of when the DevGroupShare command would be used is when you have a group of lights that you control with a dimmer switch and home automation software. You want the dimmer switch to be able to control all items. The home automation software controls each light individually. When it controls the whole group, it actually sends command to each light in the group. If you use the home automation software to turn an individual light on or off or change it’s brightness, color or scheme, you do not want the change to be replicated to the other lights. In this case, you would set the incoming and outgoing item masks to 0xffffffff (all items) on the dimmer switch (DevGroupShare 0xffffffff,0xffffffff) and set the incoming item mask to 0xffffffff and outgoing item mask to 0 on all the lights (DevGroupShare 0xffffffff,0).
### Commands
@@ -29,7 +29,7 @@ The items that are sent to the group and the items that are received from the gr
DevGroupShare
|
- ,<out> = set incoming and outgoing shared item mask (default = 255,255)
+ ,<out> = set incoming and outgoing shared item mask (default = 0xffffffff,0xffffffff)
1 = Power, 2 = Light brightness, 4 = Light fade/speed, 8 = Light scheme, 16 = Light color, 32 = Minimum brightness
|
@@ -42,4 +42,4 @@ The items that are sent to the group and the items that are received from the gr
= set device group <x> MQTT group topic (32 chars max) and restart
|
-
+
\ No newline at end of file
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 972af6974..514cf7bdd 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -104,7 +104,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t alexa_ct_range : 1; // bit 0 (v8.1.0.2) - SetOption82 - Reduced CT range for Alexa
uint32_t zigbee_use_names : 1; // bit 1 (v8.1.0.4) - SetOption83 - Use FriendlyNames instead of ShortAddresses when possible
uint32_t awsiot_shadow : 1; // bit 2 (v8.1.0.5) - SetOption84 - (AWS IoT) publish MQTT state to a device shadow
- uint32_t spare03 : 1;
+ uint32_t device_groups_enabled : 1; // bit 3 (v8.1.0.10) - SetOption85 - Enable device groups
uint32_t spare04 : 1;
uint32_t spare05 : 1;
uint32_t spare06 : 1;
@@ -467,8 +467,10 @@ struct SYSCFG {
uint8_t hotplug_scan; // F03
uint8_t reserved1; // F04 - reserved for s-hadinger
- uint8_t free_f05[207]; // F05
+ uint8_t free_f05[199]; // F05
+ uint32_t device_group_share_in; // FCC - Bitmask of device group items imported
+ uint32_t device_group_share_out; // FD0 - Bitmask of device group items exported
uint32_t bootcount_reset_time; // FD4
int adc_param4; // FD8
uint32_t shutter_button[MAX_KEYS]; // FDC
@@ -573,9 +575,4 @@ typedef union {
ADC_MODE(ADC_VCC); // Set ADC input for Power Supply Voltage usage
#endif
-// Settings re-purposed for device groups
-#define device_groups_enabled ws_clock_reverse // SetOption16 - Enable device groups
-#define device_group_share_in domoticz_sensor_idx[0] // Bitmask of device group items imported
-#define device_group_share_out domoticz_sensor_idx[1] // Bitmask of device group items exported
-
#endif // _SETTINGS_H_
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 772e22eeb..59d6b11d5 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -1677,7 +1677,7 @@ void CmndDevGroupShare(void)
ParseParameters(2, parm);
Settings.device_group_share_in = parm[0];
Settings.device_group_share_out = parm[1];
- Response_P(PSTR("{\"" D_CMND_DEVGROUP_SHARE "\":{\"In\":%d,\"Out\":%d}}"), Settings.device_group_share_in, Settings.device_group_share_out);
+ Response_P(PSTR("{\"" D_CMND_DEVGROUP_SHARE "\":{\"In\":%x,\"Out\":%x}}"), Settings.device_group_share_in, Settings.device_group_share_out);
}
#endif // USE_DEVICE_GROUPS
diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino
index 336b13cad..63775d0c3 100644
--- a/tasmota/support_device_groups.ino
+++ b/tasmota/support_device_groups.ino
@@ -49,7 +49,7 @@ struct device_group {
struct device_group * device_groups;
uint16_t outgoing_sequence = 0;
-bool initialized = false;
+bool device_groups_active = false;
bool building_status_message = false;
bool processing_remote_device_message = false;
bool waiting_for_acks;
@@ -60,36 +60,33 @@ void DeviceGroupsInit(void)
/*
Initialize the device information for each device group. The group name is the MQTT group topic.
*/
- if (Settings.flag.device_groups_enabled) {
- device_groups = (struct device_group *)calloc(device_group_count, sizeof(struct device_group));
- if (device_groups == nullptr) {
- AddLog_P2(LOG_LEVEL_ERROR, "DGR: error allocating %u-element device group array", device_group_count);
- Settings.flag.device_groups_enabled = false;
- return;
- }
-
- for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) {
- struct device_group * device_group = &device_groups[device_group_index];
- strcpy(device_group->group_name, SettingsText((device_group_index == 0 ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + device_group_index - 1)));
- device_group->message_header_length = sprintf(device_group->message, "%s%s HTTP/1.1\n\n", kDeviceGroupMessage, device_group->group_name);
- device_group->last_full_status_sequence = -1;
- }
-
- device_groups[0].local = true;
-
- // If both in and out shared items masks are 0, assume they're unitialized and initialize them.
- if (!Settings.device_group_share_in && !Settings.device_group_share_out) {
- Settings.device_group_share_in = Settings.device_group_share_out = 0xff;
- }
-
- initialized = true;
+ device_groups = (struct device_group *)calloc(device_group_count, sizeof(struct device_group));
+ if (device_groups == nullptr) {
+ AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: error allocating %u-element device group array"), device_group_count);
+ return;
}
+
+ for (uint32_t device_group_index = 0; device_group_index < device_group_count; device_group_index++) {
+ struct device_group * device_group = &device_groups[device_group_index];
+ strcpy(device_group->group_name, SettingsText((device_group_index == 0 ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + device_group_index - 1)));
+ device_group->message_header_length = sprintf_P(device_group->message, PSTR("%s%s HTTP/1.1\n\n"), kDeviceGroupMessage, device_group->group_name);
+ device_group->last_full_status_sequence = -1;
+ }
+
+ device_groups[0].local = true;
+
+ // If both in and out shared items masks are 0, assume they're unitialized and initialize them.
+ if (!Settings.device_group_share_in && !Settings.device_group_share_out) {
+ Settings.device_group_share_in = Settings.device_group_share_out = 0xffffffff;
+ }
+
+ device_groups_active = true;
}
char * IPAddressToString(const IPAddress& ip_address)
{
static char buffer[16];
- sprintf(buffer, "%u.%u.%u.%u", ip_address[0], ip_address[1], ip_address[2], ip_address[3]);
+ sprintf_P(buffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]);
return buffer;
}
@@ -131,13 +128,13 @@ void SendDeviceGroupPacket(IPAddress ip, char * packet, int len, const char * la
}
delay(10);
}
- AddLog_P2(LOG_LEVEL_ERROR, "DGR: error sending %s packet", label);
+ AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: error sending %s packet"), label);
}
void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType message_type, ...)
{
- // If device groups are not enabled, ignore this request.
- if (!Settings.flag.device_groups_enabled) return;
+ // If device groups are not active, ignore this request.
+ if (!device_groups_active) return;
// If UDP is not set up, ignore this request.
if (!udp_connected) return;
@@ -152,7 +149,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
if (device_group->initial_status_requests_remaining) return;
// 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 broadcast 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.
char * message_ptr = &device_group->message[device_group->message_header_length];
if (message_type == DGR_MSGTYP_FULL_STATUS) {
@@ -164,7 +161,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
// Call the drivers to build the status update.
if (!++outgoing_sequence) outgoing_sequence = 1;
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "Building device group %s full status packet", device_group->group_name);
+ 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);
@@ -191,7 +188,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
va_list ap;
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "Building device group %s packet", device_group->group_name);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Building device group %s packet"), device_group->group_name);
#endif // DEVICE_GROUPS_DEBUG
uint16_t original_sequence = outgoing_sequence;
if (!building_status_message && message_type != DGR_MSGTYP_PARTIAL_UPDATE && !++outgoing_sequence) outgoing_sequence = 1;
@@ -204,7 +201,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
else if (message_type == DGR_MSGTYP_UPDATE_DIRECT)
value |= DGR_FLAG_DIRECT;
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, ">sequence=%u, flags=%u", outgoing_sequence, value);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">sequence=%u, flags=%u"), outgoing_sequence, value);
#endif // DEVICE_GROUPS_DEBUG
*message_ptr++ = value & 0xff;
*message_ptr++ = value >> 8;
@@ -303,7 +300,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
}
}
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "%u items carried over from previous update", kept_item_count);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%u items carried over from previous update"), kept_item_count);
#endif // DEVICE_GROUPS_DEBUG
}
@@ -316,7 +313,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
value = va_arg(ap, int);
if (shared) {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, ">item=%u, value=%u", item, value);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%u"), item, value);
#endif // DEVICE_GROUPS_DEBUG
*message_ptr++ = value & 0xff;
if (item > DGR_ITEM_MAX_8BIT) {
@@ -335,7 +332,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
if (shared) {
value = strlen((const char *)value_ptr);
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, ">item=%u, value=%s", item, value_ptr);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%s"), item, value_ptr);
#endif // DEVICE_GROUPS_DEBUG
*message_ptr++ = value;
memcpy(message_ptr, value_ptr, value);
@@ -348,7 +345,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
value_ptr = va_arg(ap, uint8_t *);
if (shared) {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, ">item=%u, value=%u,%u,%u,%u,%u", item, *value_ptr, *(value_ptr + 1), *(value_ptr + 2), *(value_ptr + 3), *(value_ptr + 4));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(">item=%u, value=%u,%u,%u,%u,%u"), item, *value_ptr, *(value_ptr + 1), *(value_ptr + 2), *(value_ptr + 3), *(value_ptr + 4));
#endif // DEVICE_GROUPS_DEBUG
memmove(message_ptr, value_ptr, 5);
message_ptr += 5;
@@ -374,11 +371,11 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
if (building_status_message || message_type == DGR_MSGTYP_PARTIAL_UPDATE) return;
}
- // Broadcast the packet.
+ // Multicast the packet.
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "DGR: sending %u-byte device group %s packet via broadcast, 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);
+ 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
- SendDeviceGroupPacket(IPAddress(239,255,255,250), device_group->message, device_group->message_length, "Broadcast");
+ SendDeviceGroupPacket(IPAddress(239,255,255,250), device_group->message, device_group->message_length, PSTR("Multicast"));
device_group->next_ack_check_time = millis() + 100;
if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) {
@@ -393,6 +390,9 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType
void ProcessDeviceGroupMessage(char * packet, int packet_length)
{
+ // If device groups are not active, ignore this request.
+ if (!device_groups_active) return;
+
// Make the group name a null-terminated string.
char * message_group_name = packet + sizeof(DEVICE_GROUP_MESSAGE) - 1;
char * message_ptr = strchr(message_group_name, ' ');
@@ -418,11 +418,11 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length)
if (!device_group_member) {
device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member));
if (device_group_member == nullptr) {
- AddLog_P2(LOG_LEVEL_ERROR, "DGR: error allocating device group member block");
+ AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: error allocating device group member block"));
return;
}
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "DGR: adding member %s (%p)", IPAddressToString(remote_ip), device_group_member);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: adding member %s (%p)"), IPAddressToString(remote_ip), device_group_member);
#endif // DEVICE_GROUPS_DEBUG
device_group_member->ip_address = remote_ip;
*flink = device_group_member;
@@ -435,7 +435,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length)
}
// Find the start of the actual message (after the http header).
- message_ptr = strstr(message_ptr, "\n\n");
+ message_ptr = strstr_P(message_ptr, PSTR("\n\n"));
if (message_ptr == nullptr) return;
message_ptr += 2;
@@ -452,7 +452,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length)
flags = *message_ptr++;
flags |= *message_ptr++ << 8;
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "Received device group %s packet from %s: sequence=%u, flags=%u", device_group->group_name, IPAddressToString(remote_ip), message_sequence, flags);
+ 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 ack message, save the message sequence if it's newwer than the last ack we
@@ -463,7 +463,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length)
}
device_group_member->timeout_time = 0;
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "received_sequence && device_group_member->received_sequence - message_sequence > 64536) {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "group_name, IPAddressToString(remote_ip));
+ 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
@@ -555,7 +555,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length)
light_fade = value;
}
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "- = packet_length - (message_ptr - packet)) goto badmsg; // Malformed message
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "
- next_ack_check_time <= now) {
if (device_group->initial_status_requests_remaining) {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "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
if (--device_group->initial_status_requests_remaining) {
- SendDeviceGroupPacket(IPAddress(239,255,255,250), device_group->message, device_group->message_length, "Initial");
+ SendDeviceGroupPacket(IPAddress(239,255,255,250), device_group->message, device_group->message_length, PSTR("Initial"));
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;
waiting_for_acks = true;
@@ -671,16 +672,16 @@ void DeviceGroupsLoop(void)
if (device_group_member->timeout_time && device_group_member->timeout_time < now) {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "DGR: removing member %s (%p)", IPAddressToString(device_group_member->ip_address), device_group_member);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: removing member %s (%p)"), IPAddressToString(device_group_member->ip_address), device_group_member);
#endif // DEVICE_GROUPS_DEBUG
*flink = device_group_member->flink;
free(device_group_member);
}
else {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P2(LOG_LEVEL_DEBUG, "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);
+ 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, "Unicast");
+ SendDeviceGroupPacket(device_group_member->ip_address, device_group->message, device_group->message_length, PSTR("Unicast"));
if (!device_group_member->timeout_time) device_group_member->timeout_time = now + 15000;
acked = false;
flink = &device_group_member->flink;
diff --git a/tasmota/support_udp.ino b/tasmota/support_udp.ino
index 6f1d12c2e..9ff745364 100644
--- a/tasmota/support_udp.ino
+++ b/tasmota/support_udp.ino
@@ -137,7 +137,7 @@ void PollUdp(void)
}
#ifdef USE_DEVICE_GROUPS
- if (Settings.flag.device_groups_enabled && !strncmp_P(packet_buffer, kDeviceGroupMessage, sizeof(DEVICE_GROUP_MESSAGE) - 1)) {
+ if (Settings.flag4.device_groups_enabled && !strncmp_P(packet_buffer, kDeviceGroupMessage, sizeof(DEVICE_GROUP_MESSAGE) - 1)) {
ProcessDeviceGroupMessage(packet_buffer, len);
}
#endif // USE_DEVICE_GROUPS
diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino
index 6d902e2a9..dd8d2441c 100644
--- a/tasmota/support_wifi.ino
+++ b/tasmota/support_wifi.ino
@@ -560,7 +560,7 @@ void WifiCheck(uint8_t param)
}
#ifdef USE_EMULATION
#ifdef USE_DEVICE_GROUPS
- if (Settings.flag2.emulation || Settings.flag.device_groups_enabled) { UdpConnect(); }
+ if (Settings.flag2.emulation || Settings.flag4.device_groups_enabled) { UdpConnect(); }
#else // USE_DEVICE_GROUPS
if (Settings.flag2.emulation) { UdpConnect(); }
#endif // USE_DEVICE_GROUPS
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 8d17a0eb4..8e9546ed6 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -290,9 +290,6 @@ void setup(void)
#ifdef USE_ARDUINO_OTA
ArduinoOTAInit();
#endif // USE_ARDUINO_OTA
-#ifdef USE_DEVICE_GROUPS
- DeviceGroupsInit();
-#endif // USE_DEVICE_GROUPS
XdrvCall(FUNC_INIT);
XsnsCall(FUNC_INIT);
diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h
index 39ceb8208..e27213b5a 100644
--- a/tasmota/tasmota_post.h
+++ b/tasmota/tasmota_post.h
@@ -249,6 +249,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem)
#undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common)
#undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common)
+#undef USE_DEVICE_GROUPS // Disable support for device groups (+3k5 code)
#undef DEBUG_THEO // Disable debug code
#undef USE_DEBUG_DRIVER // Disable debug code
#endif // FIRMWARE_KNX_NO_EMULATION
@@ -286,6 +287,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
#undef USE_HOTPLUG // Disable support for HotPlug
+#undef USE_DEVICE_GROUPS // Disable support for device groups (+3k5 code)
#undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code)
#undef USE_PZEM004T // Disable PZEM004T energy sensor
@@ -362,6 +364,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
#undef USE_HOTPLUG // Disable support for HotPlug
+#undef USE_DEVICE_GROUPS // Disable support for device groups (+3k5 code)
// -- Optional light modules ----------------------
//#undef USE_LIGHT // Also disable all Dimmer/Light support
@@ -583,6 +586,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
#undef USE_HOTPLUG // Disable support for HotPlug
+#undef USE_DEVICE_GROUPS // Disable support for device groups (+3k5 code)
// -- Optional light modules ----------------------
#undef USE_LIGHT // Disable support for lights
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index 3eaa24ef5..e8c728024 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2971,7 +2971,7 @@ bool Xdrv01(uint8_t function)
PollDnsWebserver();
#ifdef USE_EMULATION
#ifdef USE_DEVICE_GROUPS
- if (Settings.flag2.emulation || Settings.flag.device_groups_enabled) { PollUdp(); }
+ if (Settings.flag2.emulation || Settings.flag4.device_groups_enabled) { PollUdp(); }
#else // USE_DEVICE_GROUPS
if (Settings.flag2.emulation) { PollUdp(); }
#endif // USE_DEVICE_GROUPS