From 2a367512d7375c8f3a1fe9f24a718577f1dabc1f Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Mon, 6 Apr 2020 12:29:50 -0500 Subject: [PATCH] Add DevGroupName command --- Device_Groups.md | 8 +++--- tasmota/i18n.h | 1 + tasmota/support_command.ino | 19 +++++++++++-- tasmota/support_device_groups.ino | 42 ++++++++++++++++++----------- tasmota/tasmota.h | 45 ++++++++++++++++--------------- tasmota/xdrv_04_light.ino | 6 ++--- tasmota/xdrv_35_pwm_dimmer.ino | 7 ++--- 7 files changed, 78 insertions(+), 50 deletions(-) diff --git a/Device_Groups.md b/Device_Groups.md index 40bca68a74a6..c109865af35a 100644 --- a/Device_Groups.md +++ b/Device_Groups.md @@ -11,7 +11,7 @@ To enable device groups, execute the command SetOption85 1. ## Device Groups Operation -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 device group name is set with the DevGroupName command. If the device group name is not set for a group, the MQTT group topic is used (with the device group number appended for device group numbers > 1). All devices in the same IP network with the same device group name 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 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). @@ -34,10 +34,10 @@ The items that are sent to the group and the items that are received from the gr - GroupTopic<x> + DevGroupName<x> - 1 = reset device group <x> MQTT group topic to firmware default (MQTT_GRPTOPIC) and restart
-<value> = set device group <x> MQTT group topic (32 chars max) and restart + 0 = clear device group <x> name and restart
+<value> = set device group <x>name and restart \ No newline at end of file diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 7f225838e8d5..6a6f04cecb7c 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -293,6 +293,7 @@ #define D_CMND_SPEEDUNIT "SpeedUnit" #define D_CMND_I2CSCAN "I2CScan" #define D_CMND_I2CDRIVER "I2CDriver" +#define D_CMND_DEVGROUP_NAME "DevGroupName" #define D_CMND_DEVGROUP_SHARE "DevGroupShare" #define D_CMND_SERIALSEND "SerialSend" #define D_CMND_SERIALDELIMITER "SerialDelimiter" diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 64fd45997acc..9898b5044c27 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -32,7 +32,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" #endif #ifdef USE_DEVICE_GROUPS - D_CMND_DEVGROUP_SHARE "|" + D_CMND_DEVGROUP_NAME "|" D_CMND_DEVGROUP_SHARE "|" #endif // USE_DEVICE_GROUPS D_CMND_SENSOR "|" D_CMND_DRIVER; @@ -51,7 +51,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndI2cScan, CmndI2cDriver, #endif #ifdef USE_DEVICE_GROUPS - &CmndDevGroupShare, + &CmndDevGroupName, &CmndDevGroupShare, #endif // USE_DEVICE_GROUPS &CmndSensor, &CmndDriver }; @@ -1707,6 +1707,21 @@ void CmndI2cDriver(void) #endif // USE_I2C #ifdef USE_DEVICE_GROUPS +void CmndDevGroupName(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DEV_GROUP_NAMES)) { + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len > TOPSZ) + XdrvMailbox.data[TOPSZ - 1] = 0; + else if (1 == XdrvMailbox.data_len && ('"' == XdrvMailbox.data[0] || '0' == XdrvMailbox.data[0])) + XdrvMailbox.data[0] = 0; + SettingsUpdateText(SET_DEV_GROUP_NAME1 + XdrvMailbox.index - 1, XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndAll(SET_DEV_GROUP_NAME1, MAX_DEV_GROUP_NAMES); + } +} + void CmndDevGroupShare(void) { uint32_t parm[2] = { Settings.device_group_share_in, Settings.device_group_share_out }; diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino index 4e8b4804145e..fd9c03be97cf 100644 --- a/tasmota/support_device_groups.ino +++ b/tasmota/support_device_groups.ino @@ -62,7 +62,7 @@ bool udp_was_connected = false; void DeviceGroupsInit(void) { - // Initialize the device information for each device group. The group name is the MQTT group topic. + // Initialize the device information for each device group. 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); @@ -72,7 +72,18 @@ void DeviceGroupsInit(void) 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))); + strcpy(device_group->group_name, SettingsText(SET_DEV_GROUP_NAME1 + device_group_index)); + + // If the device group name is not set, use the MQTT group topic (with the device group index + + // 1 appended for device group indices > 0). + if (!device_group->group_name[0]) { + strcpy(device_group->group_name, SettingsText(SET_MQTT_GRP_TOPIC)); + if (device_group_index) { + char str[10]; + sprintf_P(str, PSTR("%u"), device_group_index + 1); + strcat(device_group->group_name, str); + } + } 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; } @@ -106,7 +117,7 @@ char * BeginDeviceGroupMessage(struct device_group * device_group, uint16_t flag } // Return true if we're configured to share the specified item. -bool DeviceGroupItemShared(bool incoming, uint8_t item) +bool DevGroupItemShared(bool incoming, uint8_t item) { uint8_t mask = 0; switch (item) { @@ -149,7 +160,7 @@ void SendDeviceGroupPacket(IPAddress ip, char * packet, int len, const char * la AddLog_P2(LOG_LEVEL_ERROR, PSTR("DGR: error sending %s packet"), label); } -void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType message_type, ...) +void _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType message_type, ...) { // If device groups are not enabled, ignore this request. if (!Settings.flag4.device_groups_enabled) return; @@ -184,6 +195,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType device_group->message_length = 0; SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power); XdrvMailbox.command_code = DGR_ITEM_STATUS; + XdrvMailbox.topic = (char *)&device_group_index; XdrvCall(FUNC_DEVICE_GROUP_ITEM); building_status_message = false; @@ -319,7 +331,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType // Itertate through the passed items adding them and their values to the message. va_start(ap, message_type); while ((item = va_arg(ap, int))) { - shared = DeviceGroupItemShared(false, item); + shared = DevGroupItemShared(false, item); if (shared) *message_ptr++ = item; if (item <= DGR_ITEM_MAX_32BIT) { value = va_arg(ap, int); @@ -384,10 +396,8 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType uint32_t now = millis(); if (message_type == DGR_MSGTYP_UPDATE_MORE_TO_COME) { + device_group->message_length = 0; device_group->next_ack_check_time = 0; -// for (struct device_group_member * device_group_member = device_group->device_group_members; device_group_member != nullptr; device_group_member = device_group_member->flink) { -// device_group_member->acked_sequence = outgoing_sequence; -// } } else { device_group->ack_check_interval = 100; @@ -410,7 +420,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) // Search for a device group with the target group name. If one isn't found, return. struct device_group * device_group; - uint32_t device_group_index = 0; + uint8_t device_group_index = 0; for (;;) { device_group = &device_groups[device_group_index]; if (!strcmp(message_group_name, device_group->group_name)) break; @@ -518,15 +528,16 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) bool grpflg bool usridx uint16_t command_code Item code - uint32_t index 0:15 Flags, 16:23 Device group index + 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 + 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 | device_group_index << 16; + 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 (;;) { @@ -589,7 +600,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) } } - if (DeviceGroupItemShared(true, item)) { + if (DevGroupItemShared(true, item)) { if (item == DGR_ITEM_POWER) { if (device_group->local) { uint8_t mask_devices = value >> 24; @@ -744,7 +755,7 @@ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: Ckecking next_check_time=%u, now=%u"), nex if (device_group->next_ack_check_time < next_check_time) next_check_time = device_group->next_ack_check_time; } - // If it's time to send multicast announcement for this group, send it. This is to + // If it's time to send a multicast announcement for this group, send it. This is to // announcement ourself to any members that have somehow not heard about us. We send it at // the announcement interval plus a random number of milliseconds so that even if all the // devices booted at the same time, they don't all multicast their announcements at the same @@ -753,11 +764,10 @@ 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); #endif // DEVICE_GROUPS_DEBUG if (device_group->next_announcement_time <= now) { - device_group->message_length = BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT) - device_group->message; #ifdef DEVICE_GROUPS_DEBUG AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DGR: sending %u-byte device group %s announcement"), device_group->message_length, device_group->group_name); #endif // DEVICE_GROUPS_DEBUG - SendDeviceGroupPacket(0, device_group->message, device_group->message_length, PSTR("Announcement")); + SendDeviceGroupPacket(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); } if (device_group->next_announcement_time < next_check_time) next_check_time = device_group->next_announcement_time; diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 48a85389197e..431a116ecab5 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -82,6 +82,7 @@ const uint8_t MAX_RULE_MEMS = 16; // Max number of saved vars const uint8_t MAX_FRIENDLYNAMES = 8; // Max number of Friendly names const uint8_t MAX_BUTTON_TEXT = 16; // Max number of GUI button labels const uint8_t MAX_GROUP_TOPICS = 4; // Max number of Group Topics +const uint8_t MAX_DEV_GROUP_NAMES = 4; // Max number of Device Group names const uint8_t MAX_HUE_DEVICES = 15; // Max number of Philips Hue device per emulation @@ -302,30 +303,30 @@ enum SettingsTextIndex { SET_OTAURL, SET_BUTTON1, SET_BUTTON2, SET_BUTTON3, SET_BUTTON4, SET_BUTTON5, SET_BUTTON6, SET_BUTTON7, SET_BUTTON8, SET_BUTTON9, SET_BUTTON10, SET_BUTTON11, SET_BUTTON12, SET_BUTTON13, SET_BUTTON14, SET_BUTTON15, SET_BUTTON16, SET_MQTT_GRP_TOPIC2, SET_MQTT_GRP_TOPIC3, SET_MQTT_GRP_TOPIC4, - SET_TEMPLATE_NAME, + SET_TEMPLATE_NAME, SET_DEV_GROUP_NAME1, SET_DEV_GROUP_NAME2, SET_DEV_GROUP_NAME3, SET_DEV_GROUP_NAME4, SET_MAX }; -enum DeviceGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYP_REUPDATE }; - -enum DeviceGroupMessageFlag { DGR_FLAG_RESET = 1, DGR_FLAG_STATUS_REQUEST = 2, DGR_FLAG_FULL_STATUS = 4, DGR_FLAG_ACK = 8, DGR_FLAG_MORE_TO_COME = 16, DGR_FLAG_DIRECT = 32, DGR_FLAG_ANNOUNCEMENT = 64 }; - -enum DeviceGroupItem { DGR_ITEM_EOL, DGR_ITEM_STATUS, - DGR_ITEM_LIGHT_FADE, DGR_ITEM_LIGHT_SPEED, DGR_ITEM_LIGHT_BRI, DGR_ITEM_LIGHT_SCHEME, DGR_ITEM_LIGHT_FIXED_COLOR, - DGR_ITEM_BRI_PRESET_LOW, DGR_ITEM_BRI_PRESET_HIGH, DGR_ITEM_BRI_POWER_ON, - // Add new 8-bit items before this line - DGR_ITEM_LAST_8BIT, DGR_ITEM_MAX_8BIT = 63, - DGR_ITEM_ANALOG1, DGR_ITEM_ANALOG2, DGR_ITEM_ANALOG3, DGR_ITEM_ANALOG4, DGR_ITEM_ANALOG5, - // Add new 16-bit items before this line - DGR_ITEM_LAST_16BIT, DGR_ITEM_MAX_16BIT = 127, - DGR_ITEM_POWER, DGR_ITEM_DIMMER_RANGE, - // Add new 32-bit items before this line - DGR_ITEM_LAST_32BIT, DGR_ITEM_MAX_32BIT = 191, - // Add new string items before this line - DGR_ITEM_LAST_STRING, DGR_ITEM_MAX_STRING = 223, - DGR_ITEM_LIGHT_CHANNELS }; - -enum DeviceGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE_LIGHT_FADE = 4, DGR_SHARE_LIGHT_SCHEME = 8, - DGR_SHARE_LIGHT_COLOR = 16, DGR_SHARE_DIMMER_SETTINGS = 32 }; +enum DevGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYP_REUPDATE }; + +enum DevGroupMessageFlag { DGR_FLAG_RESET = 1, DGR_FLAG_STATUS_REQUEST = 2, DGR_FLAG_FULL_STATUS = 4, DGR_FLAG_ACK = 8, DGR_FLAG_MORE_TO_COME = 16, DGR_FLAG_DIRECT = 32, DGR_FLAG_ANNOUNCEMENT = 64 }; + +enum DevGroupItem { DGR_ITEM_EOL, DGR_ITEM_STATUS, + DGR_ITEM_LIGHT_FADE, DGR_ITEM_LIGHT_SPEED, DGR_ITEM_LIGHT_BRI, DGR_ITEM_LIGHT_SCHEME, DGR_ITEM_LIGHT_FIXED_COLOR, + DGR_ITEM_BRI_PRESET_LOW, DGR_ITEM_BRI_PRESET_HIGH, DGR_ITEM_BRI_POWER_ON, + // Add new 8-bit items before this line + DGR_ITEM_LAST_8BIT, DGR_ITEM_MAX_8BIT = 63, + DGR_ITEM_ANALOG1, DGR_ITEM_ANALOG2, DGR_ITEM_ANALOG3, DGR_ITEM_ANALOG4, DGR_ITEM_ANALOG5, + // Add new 16-bit items before this line + DGR_ITEM_LAST_16BIT, DGR_ITEM_MAX_16BIT = 127, + DGR_ITEM_POWER, DGR_ITEM_DIMMER_RANGE, + // Add new 32-bit items before this line + DGR_ITEM_LAST_32BIT, DGR_ITEM_MAX_32BIT = 191, + // Add new string items before this line + DGR_ITEM_LAST_STRING, DGR_ITEM_MAX_STRING = 223, + DGR_ITEM_LIGHT_CHANNELS }; + +enum DevGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE_LIGHT_FADE = 4, DGR_SHARE_LIGHT_SCHEME = 8, + DGR_SHARE_LIGHT_COLOR = 16, DGR_SHARE_DIMMER_SETTINGS = 32 }; enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER, diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 8de0616b7330..9b83a6879a70 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -2131,14 +2131,14 @@ void LightSendDeviceGroupStatus(bool force) } } -void LightHandleDeviceGroupItem(void) +void LightHandleDevGroupItem(void) { static bool send_state = false; static bool restore_power = false; bool more_to_come; uint32_t value = XdrvMailbox.payload; #ifdef USE_PWM_DIMMER_REMOTE - if (XdrvMailbox.index & 0xff0000) return; // Ignore updates from other device groups + if (*XdrvMailbox.topic) return; // Ignore updates from other device groups #endif // USE_PWM_DIMMER_REMOTE switch (XdrvMailbox.command_code) { case DGR_ITEM_EOL: @@ -2774,7 +2774,7 @@ bool Xdrv04(uint8_t function) break; #ifdef USE_DEVICE_GROUPS case FUNC_DEVICE_GROUP_ITEM: - LightHandleDeviceGroupItem(); + LightHandleDevGroupItem(); break; #endif // USE_DEVICE_GROUPS case FUNC_SET_POWER: diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 0e0594a8b1be..24195a080e9f 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -101,6 +101,7 @@ void PWMModulePreInit(void) if (Settings.flag4.remote_device_mode) { Settings.flag4.device_groups_enabled = true; + device_group_count = 0; for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { if (pin[GPIO_KEY1 + button_index] < 99) device_group_count++; } @@ -169,11 +170,11 @@ void PWMDimmerSetPower(void) } #ifdef USE_DEVICE_GROUPS -void PWMDimmerHandleDeviceGroupItem(void) +void PWMDimmerHandleDevGroupItem(void) { uint32_t value = XdrvMailbox.payload; #ifdef USE_PWM_DIMMER_REMOTE - uint8_t device_group_index = XdrvMailbox.index >> 16 & 0xff; + uint8_t device_group_index = *(uint8_t *)XdrvMailbox.topic; bool device_is_local = device_groups[device_group_index].local; struct remote_pwm_dimmer * remote_pwm_dimmer = &remote_pwm_dimmers[device_group_index]; #endif // USE_PWM_DIMMER_REMOTE @@ -763,7 +764,7 @@ bool Xdrv35(uint8_t function) #ifdef USE_DEVICE_GROUPS case FUNC_DEVICE_GROUP_ITEM: - PWMDimmerHandleDeviceGroupItem(); + PWMDimmerHandleDevGroupItem(); break; #endif // USE_DEVICE_GROUPS