Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: The big cleanup #24200

Merged
merged 21 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
# [email protected] 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
# [email protected] requires Node 20 >=
pnpm i [email protected]
- 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
Loading