Skip to content

Commit

Permalink
feat!: The big cleanup (#24200)
Browse files Browse the repository at this point in the history
* Remove `deviceGroupMembership`

* Fix controller.ts

* Remove `legacy_api` logic from non-legacy extensions.

* Fix network map coverage.

* Remove all `legacy` extensions.

* Remove `legacy_availability_payload`, `legacy_api`. Remove legacy from configure.

* Fix prettier.

* Remove some leftovers

* Renamed `updateAvailable` to `update_available`

* Remove emitPublishAvailability

* Remove `configuration.yaml`

* Switch to pnpm

* Remove direct mqtt-packet dependency.

* fix pretty

* fix `getDependencyVersion`

* fix pnpm publish

* fix(ignore): fix pnpm publish

* Remove deprecated bridge `config/*`

* Improve update script

* Improve update.sh

---------

Co-authored-by: Nerivec <62446222+Nerivec@users.noreply.github.com>
  • Loading branch information
Koenkk and Nerivec committed Dec 1, 2024
1 parent 3220b27 commit b618662
Show file tree
Hide file tree
Showing 50 changed files with 6,459 additions and 12,214 deletions.
31 changes: 18 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,24 @@ jobs:
token: ${{secrets.GH_TOKEN}}
- uses: actions/checkout@v4
if: ((github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push') == false
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org/
cache: npm
cache: pnpm
- name: Install dependencies
run: npm ci
run: pnpm i --frozen-lockfile
- name: Build
run: npm run build
run: pnpm run build
- name: Lint
run: |
npm run pretty:check
npm run eslint
pnpm run pretty:check
pnpm run eslint
- name: Test
run: npm run test-with-coverage
run: pnpm run test-with-coverage
- name: Docker login
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
run: echo ${{ secrets.DOCKER_KEY }} | docker login -u koenkk --password-stdin
Expand Down Expand Up @@ -74,7 +77,7 @@ jobs:
.
- name: 'release: Publish to npm'
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
run: npm publish
run: pnpm publish --no-git-checks
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN }}
- name: 'dev: Trigger zigbee2mqtt/hassio-zigbee2mqtt build'
Expand Down Expand Up @@ -139,15 +142,17 @@ jobs:
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
registry-url: https://registry.npmjs.org/
cache: 'npm'
node-version: 20
cache: pnpm
- name: Install dependencies
# --ignore-scripts prevents the serialport build which often fails on Windows
run: npm ci --ignore-scripts
run: pnpm i --frozen-lockfile --ignore-scripts
- name: Build
run: npm run build
run: pnpm run build
- name: Test
run: npm run test-with-coverage
run: pnpm run test-with-coverage
2 changes: 1 addition & 1 deletion .github/workflows/release_please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
MASTER_FRONTEND_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee2mqtt-frontend"')
wget -q -O - https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/release-please--branches--dev--components--zigbee2mqtt/CHANGELOG.md > z2m/CHANGELOG.md
cd z2m
npm ci
pnpm i --frozen-lockfile
node scripts/generateChangelog.js $MASTER_Z2M_VERSION $MASTER_ZHC_VERSION $MASTER_ZH_VERSION $MASTER_FRONTEND_VERSION >> ../changelog.md
env:
GH_TOKEN: ${{secrets.GH_TOKEN}}
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/update_dep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ jobs:
with:
ref: dev
token: ${{ secrets.GH_TOKEN }}
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm install ${{ github.event.client_payload.package }}@${{ github.event.client_payload.version }} --save-exact
cache: pnpm
- run: pnpm install ${{ github.event.client_payload.package }}@${{ github.event.client_payload.version }} --save-exact
- uses: peter-evans/create-pull-request@v7
id: cpr
with:
Expand Down
13 changes: 8 additions & 5 deletions .github/workflows/update_deps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ jobs:
with:
ref: dev
token: ${{ secrets.GH_TOKEN }}
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
# connect-gzip-static@4.0.0 requires Node 20 >=
- run: npx npm-check-updates -u -x connect-gzip-static
- run: rm -f package-lock.json
- run: npm install
cache: pnpm
- run: |
pnpm up --latest
# connect-gzip-static@4.0.0 requires Node 20 >=
pnpm i connect-gzip-static@3.0.1
- uses: peter-evans/create-pull-request@v7
with:
commit-message: 'fix(ignore): update dependencies'
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
package-lock.json
pnpm-lock.yaml
CHANGELOG.md
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ Everybody is invited and welcome to contribute to Zigbee2MQTT. Zigbee2MQTT is wr

- Pull requests are always created against the [**dev**](https://github.com/Koenkk/zigbee2mqtt/tree/dev) branch.
- Easiest way to start developing Zigbee2MQTT is by setting up a development environment (aka bare-metal installation). You can follow this [guide](https://www.zigbee2mqtt.io/guide/installation/01_linux.html) to do this.
- You can run the tests locally by executing `npm test`. Zigbee2MQTT enforces 100% code coverage, in case you add new code check if your code is covered by running `npm run test-with-coverage`. The coverage report can be found under `coverage/lcov-report/index.html`. Linting is also enforced and can be run with `npm run eslint`.
- You can run the tests locally by executing `pnpm test`. Zigbee2MQTT enforces 100% code coverage, in case you add new code check if your code is covered by running `pnpm run test-with-coverage`. The coverage report can be found under `coverage/lcov-report/index.html`. Linting is also enforced and can be run with `pnpm run eslint`.
- When you want to add support for a new device no changes to Zigbee2MQTT have to be made, only to zigbee-herdsman-converters. You can find a guide for it [here](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ Zigbee2MQTT is made up of three modules, each developed in its own Github projec

### Developing

Zigbee2MQTT uses TypeScript (partially for now). Therefore after making changes to files in the `lib/` directory you need to recompile Zigbee2MQTT. This can be done by executing `npm run build`. For faster development instead of running `npm run build` you can run `npm run build-watch` in another terminal session, this will recompile as you change files.
In first time before building you need to run `npm install --include=dev`
Before submitting changes run `npm run test-with-coverage`, `npm run pretty:check` and `npm run eslint`
Zigbee2MQTT uses TypeScript (partially for now). Therefore after making changes to files in the `lib/` directory you need to recompile Zigbee2MQTT. This can be done by executing `pnpm run build`. For faster development instead of running `pnpm run build` you can run `pnpm run build-watch` in another terminal session, this will recompile as you change files.
In first time before building you need to run `pnpm install --include=dev`
Before submitting changes run `pnpm run test-with-coverage`, `pnpm run pretty:check` and `pnpm run eslint`

## Supported devices

Expand Down
20 changes: 0 additions & 20 deletions data/configuration.yaml

This file was deleted.

8 changes: 4 additions & 4 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ FROM base as dependencies_and_build
COPY package*.json tsconfig.json index.js ./
COPY lib ./lib

RUN apk add --no-cache --virtual .buildtools make gcc g++ python3 linux-headers git npm && \
npm ci --production --no-audit --no-optional --no-update-notifier && \
RUN apk add make gcc g++ python3 linux-headers git npm && \
npm install -g pnpm && \
pnpm install --frozen-lockfile --no-optional && \
# Serialport needs to be rebuild for Alpine https://serialport.io/docs/9.x.x/guide-installation#alpine-linux
npm rebuild --build-from-source && \
apk del .buildtools
pnpm rebuild

# Release
FROM base as release
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ async function build(reason) {
env.NODE_OPTIONS = '--max_old_space_size=256';
}

exec('npm run build', {env, cwd: __dirname}, async (err, stdout, stderr) => {
exec('pnpm run build', {env, cwd: __dirname}, async (err, stdout, stderr) => {
if (err) {
process.stdout.write(', failed\n');

Expand Down
33 changes: 2 additions & 31 deletions lib/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ import ExtensionExternalExtension from './extension/externalExtension';
import ExtensionFrontend from './extension/frontend';
import ExtensionGroups from './extension/groups';
import ExtensionHomeAssistant from './extension/homeassistant';
import ExtensionBridgeLegacy from './extension/legacy/bridgeLegacy';
import ExtensionDeviceGroupMembership from './extension/legacy/deviceGroupMembership';
import ExtensionReport from './extension/legacy/report';
import ExtensionSoftReset from './extension/legacy/softReset';
import ExtensionNetworkMap from './extension/networkMap';
import ExtensionOnEvent from './extension/onEvent';
import ExtensionOTAUpdate from './extension/otaUpdate';
Expand All @@ -41,15 +37,11 @@ const AllExtensions = [
ExtensionPublish,
ExtensionReceive,
ExtensionNetworkMap,
ExtensionSoftReset,
ExtensionHomeAssistant,
ExtensionConfigure,
ExtensionDeviceGroupMembership,
ExtensionBridgeLegacy,
ExtensionBridge,
ExtensionGroups,
ExtensionBind,
ExtensionReport,
ExtensionOnEvent,
ExtensionOTAUpdate,
ExtensionExternalConverters,
Expand Down Expand Up @@ -108,13 +100,11 @@ export class Controller {
new ExtensionBridge(...this.extensionArgs),
new ExtensionPublish(...this.extensionArgs),
new ExtensionReceive(...this.extensionArgs),
new ExtensionDeviceGroupMembership(...this.extensionArgs),
new ExtensionConfigure(...this.extensionArgs),
new ExtensionNetworkMap(...this.extensionArgs),
new ExtensionGroups(...this.extensionArgs),
new ExtensionBind(...this.extensionArgs),
new ExtensionOTAUpdate(...this.extensionArgs),
new ExtensionReport(...this.extensionArgs),
new ExtensionExternalExtension(...this.extensionArgs),
new ExtensionAvailability(...this.extensionArgs),
];
Expand All @@ -123,22 +113,13 @@ export class Controller {
this.extensions.push(new ExtensionFrontend(...this.extensionArgs));
}

if (settings.get().advanced.legacy_api) {
this.extensions.push(new ExtensionBridgeLegacy(...this.extensionArgs));
}

if (settings.get().external_converters.length) {
this.extensions.push(new ExtensionExternalConverters(...this.extensionArgs));
}

if (settings.get().homeassistant) {
this.extensions.push(new ExtensionHomeAssistant(...this.extensionArgs));
}

/* istanbul ignore next */
if (settings.get().advanced.soft_reset_timeout !== 0) {
this.extensions.push(new ExtensionSoftReset(...this.extensionArgs));
}
}

async start(): Promise<void> {
Expand All @@ -156,9 +137,8 @@ export class Controller {
}

// Start zigbee
let startResult;
try {
startResult = await this.zigbee.start();
await this.zigbee.start();
this.eventBus.onAdapterDisconnected(this, this.onZigbeeAdapterDisconnected);
} catch (error) {
logger.error('Failed to start zigbee-herdsman');
Expand All @@ -172,15 +152,6 @@ export class Controller {
return await this.exit(1);
}

// Disable some legacy options on new network creation
if (startResult === 'reset') {
settings.set(['advanced', 'homeassistant_legacy_entity_attributes'], false);
settings.set(['advanced', 'legacy_api'], false);
settings.set(['advanced', 'legacy_availability_payload'], false);
settings.set(['device_options', 'legacy'], false);
await this.enableDisableExtension(false, 'BridgeLegacy');
}

// Log zigbee clients on startup
let deviceCount = 0;

Expand Down Expand Up @@ -356,7 +327,7 @@ export class Controller {
// Filter mqtt message attributes
utils.filterProperties(entity.options.filtered_attributes, message);

if (Object.entries(message).length) {
if (!utils.objectIsEmpty(message)) {
const output = settings.get().advanced.output;
if (output === 'attribute_and_json' || output === 'json') {
await this.mqtt.publish(entity.name, stringify(message), options);
Expand Down
7 changes: 0 additions & 7 deletions lib/eventBus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,6 @@ export default class EventBus {
this.on('permitJoinChanged', callback, key);
}

public emitPublishAvailability(): void {
this.emitter.emit('publishAvailability');
}
public onPublishAvailability(key: ListenerKey, callback: () => void): void {
this.on('publishAvailability', callback, key);
}

public emitEntityRenamed(data: eventdata.EntityRenamed): void {
this.emitter.emit('deviceRenamed', data);
}
Expand Down
3 changes: 1 addition & 2 deletions lib/extension/availability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ export default class Availability extends Extension {
this.eventBus.onDeviceLeave(this, (data) => clearTimeout(this.timers[data.ieeeAddr]));
this.eventBus.onDeviceAnnounce(this, (data) => this.retrieveState(data.device));
this.eventBus.onLastSeenChanged(this, this.onLastSeenChanged);
this.eventBus.onPublishAvailability(this, this.publishAvailabilityForAllEntities);
this.eventBus.onGroupMembersChanged(this, (data) => this.publishAvailability(data.group, false));
// Publish initial availability
await this.publishAvailabilityForAllEntities();
Expand Down Expand Up @@ -189,7 +188,7 @@ export default class Availability extends Extension {
}

const topic = `${entity.name}/availability`;
const payload = utils.availabilityPayload(available ? 'online' : 'offline', settings.get());
const payload = JSON.stringify({state: available ? 'online' : 'offline'});
this.availabilityCache[entity.ID] = available;
await this.mqtt.publish(topic, payload, {retain: true, qos: 1});

Expand Down
38 changes: 3 additions & 35 deletions lib/extension/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import * as settings from '../util/settings';
import utils from '../util/utils';
import Extension from './extension';

const LEGACY_API = settings.get().advanced.legacy_api;
const LEGACY_TOPIC_REGEX = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/(bind|unbind)/.+$`);
const TOPIC_REGEX = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/(bind|unbind)`);
const ALL_CLUSTER_CANDIDATES: readonly ClusterName[] = [
'genScenes',
Expand Down Expand Up @@ -226,12 +224,7 @@ export default class Bind extends Extension {
let clusters: ParsedMQTTMessage['clusters'] | undefined;
let skipDisableReporting: ParsedMQTTMessage['skipDisableReporting'] = false;

if (LEGACY_API && data.topic.match(LEGACY_TOPIC_REGEX)) {
const topic = data.topic.replace(`${settings.get().mqtt.base_topic}/bridge/`, '');
type = topic.split('/')[0] as ParsedMQTTMessage['type'];
sourceKey = topic.replace(`${type}/`, '');
targetKey = data.message;
} else if (data.topic.match(TOPIC_REGEX)) {
if (data.topic.match(TOPIC_REGEX)) {
type = data.topic.endsWith('unbind') ? 'unbind' : 'bind';
const message: DataMessage = JSON.parse(data.message);
sourceKey = message.from;
Expand Down Expand Up @@ -312,37 +305,16 @@ export default class Bind extends Extension {
logger.info(
`Successfully ${type === 'bind' ? 'bound' : 'unbound'} cluster '${cluster}' from '${source.name}' to '${target.name}'`,
);

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
await this.mqtt.publish(
'bridge/log',
stringify({type: `device_${type}`, message: {from: source.name, to: target.name, cluster}}),
);
}
} catch (error) {
failedClusters.push(cluster);
logger.error(`Failed to ${type} cluster '${cluster}' from '${source.name}' to '${target.name}' (${error})`);

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
await this.mqtt.publish(
'bridge/log',
stringify({type: `device_${type}_failed`, message: {from: source.name, to: target.name, cluster}}),
);
}
}
}
}

if (attemptedClusters.length === 0) {
logger.error(`Nothing to ${type} from '${source.name}' to '${target.name}'`);
error = `Nothing to ${type}`;

/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
await this.mqtt.publish('bridge/log', stringify({type: `device_${type}_failed`, message: {from: source.name, to: target.name}}));
}
} else if (failedClusters.length === attemptedClusters.length) {
error = `Failed to ${type}`;
}
Expand All @@ -359,13 +331,9 @@ export default class Bind extends Extension {
}
}

const triggeredViaLegacyApi = data.topic.match(LEGACY_TOPIC_REGEX);
const response = utils.getResponse(message, responseData, error);

if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);

await this.mqtt.publish(`bridge/response/device/${type}`, stringify(response));
}
await this.mqtt.publish(`bridge/response/device/${type}`, stringify(response));

if (error) {
logger.error(error);
Expand Down
Loading

0 comments on commit b618662

Please sign in to comment.