Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Koenkk committed Oct 15, 2024
1 parent 5a699dd commit 866e30a
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 294 deletions.
6 changes: 0 additions & 6 deletions lib/extension/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,11 @@ export default class Groups extends Extension {
assert(resolvedEntityGroup, '`resolvedEntityGroup` is missing');
logger.info(`Adding '${resolvedEntityDevice.name}' to '${resolvedEntityGroup.name}'`);
await resolvedEntityEndpoint.addToGroup(resolvedEntityGroup.zh);
settings.addDeviceToGroup(resolvedEntityGroup.ID.toString(), keys);
changedGroups.push(resolvedEntityGroup);
} else if (type === 'remove') {
assert(resolvedEntityGroup, '`resolvedEntityGroup` is missing');
logger.info(`Removing '${resolvedEntityDevice.name}' from '${resolvedEntityGroup.name}'`);
await resolvedEntityEndpoint.removeFromGroup(resolvedEntityGroup.zh);
settings.removeDeviceFromGroup(resolvedEntityGroup.ID.toString(), keys);
changedGroups.push(resolvedEntityGroup);
} else {
// remove_all
Expand All @@ -284,10 +282,6 @@ export default class Groups extends Extension {
}

await resolvedEntityEndpoint.removeFromAllGroups();

for (const settingsGroup of settings.getGroups()) {
settings.removeDeviceFromGroup(settingsGroup.ID.toString(), keys);
}
}
} catch (e) {
error = `Failed to ${type} from group (${(e as Error).message})`;
Expand Down
3 changes: 1 addition & 2 deletions lib/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ declare global {
ssl_key?: string;
};
devices: {[s: string]: DeviceOptions};
groups: {[s: string]: OptionalProps<Omit<GroupOptions, 'ID'>, 'devices'>};
groups: {[s: string]: Omit<GroupOptions, 'ID'>};
device_options: KeyValue;
advanced: {
log_rotation: boolean;
Expand Down Expand Up @@ -239,7 +239,6 @@ declare global {
}

interface GroupOptions {
devices: string[];
ID: number;
optimistic?: boolean;
off_state?: 'all_members_off' | 'last_member_state';
Expand Down
56 changes: 3 additions & 53 deletions lib/util/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,12 @@ export function getGroup(IDorName: string | number): GroupOptions | undefined {
const byID = settings.groups[IDorName];

if (byID) {
return {devices: [], ...byID, ID: Number(IDorName)};
return {...byID, ID: Number(IDorName)};
}

for (const [ID, group] of Object.entries(settings.groups)) {
if (group.friendly_name === IDorName) {
return {devices: [], ...group, ID: Number(ID)};
return {...group, ID: Number(ID)};
}
}

Expand All @@ -544,7 +544,7 @@ export function getGroups(): GroupOptions[] {
const settings = get();

return Object.entries(settings.groups).map(([ID, group]) => {
return {devices: [], ...group, ID: Number(ID)};
return {...group, ID: Number(ID)};
});
}

Expand Down Expand Up @@ -615,16 +615,6 @@ export function removeDevice(IDorName: string): void {
const device = getDeviceThrowIfNotExists(IDorName);
const settings = getInternalSettings();
delete settings.devices?.[device.ID];

// Remove device from groups
if (settings.groups) {
const regex = new RegExp(`^(${device.friendly_name}|${device.ID})(/[^/]+)?$`);

for (const group of Object.values(settings.groups).filter((g) => g.devices)) {
group.devices = group.devices?.filter((device) => !device.match(regex));
}
}

write();
}

Expand Down Expand Up @@ -662,46 +652,6 @@ export function addGroup(name: string, ID?: string): GroupOptions {
return getGroup(ID)!; // valid from creation above
}

function groupGetDevice(group: {devices?: string[]}, keys: string[]): string | undefined {
for (const device of group.devices ?? []) {
if (keys.includes(device)) {
return device;
}
}

return undefined;
}

export function addDeviceToGroup(IDorName: string, keys: string[]): void {
const groupID = getGroupThrowIfNotExists(IDorName).ID!;
const settings = getInternalSettings();

const group = settings.groups![groupID];

if (!groupGetDevice(group, keys)) {
if (!group.devices) group.devices = [];
group.devices.push(keys[0]);
write();
}
}

export function removeDeviceFromGroup(IDorName: string, keys: string[]): void {
const groupID = getGroupThrowIfNotExists(IDorName).ID!;
const settings = getInternalSettings();
const group = settings.groups![groupID];

if (!group.devices) {
return;
}

const key = groupGetDevice(group, keys);

if (key) {
group.devices = group.devices.filter((d) => d != key);
write();
}
}

export function removeGroup(IDorName: string | number): void {
const groupID = getGroupThrowIfNotExists(IDorName.toString()).ID!;
const settings = getInternalSettings();
Expand Down
71 changes: 35 additions & 36 deletions test/bridge.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ describe('Bridge', () => {
external_converters: [],
groups: {
1: {friendly_name: 'group_1', retain: false},
11: {devices: ['bulb_2'], friendly_name: 'group_with_tradfri', retain: false},
12: {devices: ['TS0601_thermostat'], friendly_name: 'thermostat_group', retain: false},
14: {devices: ['power_plug', 'bulb_2'], friendly_name: 'switch_group', retain: false},
15071: {devices: ['bulb_color_2', 'bulb_2'], friendly_name: 'group_tradfri_remote', retain: false},
11: {friendly_name: 'group_with_tradfri', retain: false},
12: {friendly_name: 'thermostat_group', retain: false},
14: {friendly_name: 'switch_group', retain: false},
15071: {friendly_name: 'group_tradfri_remote', retain: false},
2: {friendly_name: 'group_2', retain: false},
21: {devices: ['GLEDOPTO_2ID/cct'], friendly_name: 'gledopto_group'},
9: {devices: ['bulb_color_2', 'bulb_2', 'wall_switch_double/right'], friendly_name: 'ha_discovery_group'},
21: {friendly_name: 'gledopto_group'},
9: {friendly_name: 'ha_discovery_group'},
},
homeassistant: false,
map_options: {
Expand Down Expand Up @@ -2154,27 +2154,44 @@ describe('Bridge', () => {
it('Should publish groups on startup', async () => {
await resetExtension();
logger.setTransportsEnabled(true);
console.log(MQTT.publish.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/groups'));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/groups',
stringify([
{friendly_name: 'group_1', id: 1, members: [], scenes: []},
{friendly_name: 'group_tradfri_remote', id: 15071, members: [{endpoint: 1, ieee_address: '0x000b57fffec6a5b4'}], scenes: []},
{
friendly_name: 'group_tradfri_remote',
id: 15071,
members: [
{endpoint: 1, ieee_address: '0x000b57fffec6a5b4'},
{endpoint: 1, ieee_address: '0x000b57fffec6a5b7'},
],
scenes: [],
},
{friendly_name: '99', id: 99, members: [], scenes: []},
{friendly_name: 'group_with_tradfri', id: 11, members: [], scenes: []},
{friendly_name: 'thermostat_group', id: 12, members: [], scenes: []},
{friendly_name: 'switch_group', id: 14, members: [{endpoint: 1, ieee_address: '0x0017880104e45524'}], scenes: []},
{friendly_name: 'gledopto_group', id: 21, members: [], scenes: []},
{friendly_name: 'group_with_tradfri', id: 11, members: [{endpoint: 1, ieee_address: '0x000b57fffec6a5b7'}], scenes: []},
{friendly_name: 'thermostat_group', id: 12, members: [{endpoint: 1, ieee_address: '0x0017882104a44559'}], scenes: []},
{
friendly_name: 'switch_group',
id: 14,
members: [
{endpoint: 1, ieee_address: '0x0017880104e45524'},
{endpoint: 1, ieee_address: '0x000b57fffec6a5b7'},
],
scenes: [],
},
{friendly_name: 'gledopto_group', id: 21, members: [{endpoint: 15, ieee_address: '0x0017880104e45724'}], scenes: []},
{friendly_name: 'default_bind_group', id: 901, members: [], scenes: []},
{
friendly_name: 'ha_discovery_group',
id: 9,
members: [
{endpoint: 1, ieee_address: '0x000b57fffec6a5b4'},
{endpoint: 1, ieee_address: '0x000b57fffec6a5b7'},
{endpoint: 2, ieee_address: '0x0017880104e45542'},
],
scenes: [{id: 4, name: 'Scene 4'}],
},
{friendly_name: 'group_2', id: 2, members: [], scenes: []},
]),
{retain: true, qos: 0},
expect.any(Function),
Expand Down Expand Up @@ -2778,22 +2795,6 @@ describe('Bridge', () => {

it('Should allow to remove device by string', async () => {
const device = zigbeeHerdsman.devices.bulb;
settings.set(['groups'], {
1: {
friendly_name: 'group_1',
retain: false,
devices: [
'0x999b57fffec6a5b9/1',
'0x000b57fffec6a5b2/1',
'bulb',
'bulb/right',
'other_bulb',
'bulb_1',
'0x000b57fffec6a5b2',
'bulb/room/2',
],
},
});
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', 'bulb');
await flushPromises();
Expand All @@ -2810,7 +2811,6 @@ describe('Bridge', () => {
expect.any(Function),
);
expect(settings.get().blocklist).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual(['0x999b57fffec6a5b9/1', 'other_bulb', 'bulb_1', 'bulb/room/2']);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
});
Expand Down Expand Up @@ -2977,7 +2977,7 @@ describe('Bridge', () => {
MQTT.events.message('zigbee2mqtt/bridge/request/group/rename', stringify({from: 'group_1', to: 'group_new_name'}));
await flushPromises();
expect(settings.getGroup('group_1')).toBeUndefined();
expect(settings.getGroup('group_new_name')).toStrictEqual({ID: 1, devices: [], friendly_name: 'group_new_name', retain: false});
expect(settings.getGroup('group_new_name')).toStrictEqual({ID: 1, friendly_name: 'group_new_name', retain: false});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/rename',
Expand Down Expand Up @@ -3312,10 +3312,10 @@ describe('Bridge', () => {

it('Should allow change group options', async () => {
MQTT.publish.mockClear();
expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, devices: [], friendly_name: 'group_1', retain: false});
expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, friendly_name: 'group_1', retain: false});
MQTT.events.message('zigbee2mqtt/bridge/request/group/options', stringify({options: {retain: true, transition: 1}, id: 'group_1'}));
await flushPromises();
expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, devices: [], friendly_name: 'group_1', retain: true, transition: 1});
expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, friendly_name: 'group_1', retain: true, transition: 1});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/options',
stringify({data: {from: {retain: false}, to: {retain: true, transition: 1}, restart_required: false, id: 'group_1'}, status: 'ok'}),
Expand All @@ -3326,12 +3326,11 @@ describe('Bridge', () => {

it('Should allow change group options with restart required', async () => {
MQTT.publish.mockClear();
expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, devices: [], friendly_name: 'group_1', retain: false});
expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, friendly_name: 'group_1', retain: false});
MQTT.events.message('zigbee2mqtt/bridge/request/group/options', stringify({options: {off_state: 'all_members_off'}, id: 'group_1'}));
await flushPromises();
expect(settings.getGroup('group_1')).toStrictEqual({
ID: 1,
devices: [],
friendly_name: 'group_1',
retain: false,
off_state: 'all_members_off',
Expand Down Expand Up @@ -3363,7 +3362,7 @@ describe('Bridge', () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', 'group_193');
await flushPromises();
expect(settings.getGroup('group_193')).toStrictEqual({ID: 3, devices: [], friendly_name: 'group_193'});
expect(settings.getGroup('group_193')).toStrictEqual({ID: 3, friendly_name: 'group_193'});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/add',
Expand All @@ -3377,7 +3376,7 @@ describe('Bridge', () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name: 'group_193', id: 92}));
await flushPromises();
expect(settings.getGroup('group_193')).toStrictEqual({ID: 92, devices: [], friendly_name: 'group_193'});
expect(settings.getGroup('group_193')).toStrictEqual({ID: 92, friendly_name: 'group_193'});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/add',
Expand Down
Loading

0 comments on commit 866e30a

Please sign in to comment.