# GitHub Copilot Instructions

## Priority Guidelines

When generating code for this repository:

1. **Version Compatibility**: Always detect and respect the exact versions of languages, frameworks, and libraries used in this project
2. **Context Files**: Prioritize patterns and standards defined in the .github/copilot directory
3. **Codebase Patterns**: When context files don't provide specific guidance, scan the codebase for established patterns
4. **Architectural Consistency**: Maintain our layered architectural style with clear separation between controller, extensions, models, and utilities
5. **Code Quality**: Prioritize maintainability, performance, security, and testability in all generated code

## Technology Stack

### Core Technologies
- **Language**: TypeScript 5.9.3 with target `esnext` and module `NodeNext`
- **Runtime**: Node.js ^20 || ^22 || ^24
- **Package Manager**: pnpm 10.12.1
- **Testing**: Vitest 3.1.1 with @vitest/coverage-v8
- **Linting/Formatting**: Biome 2.2.5 (configured with 4-space indents, 150 line width, no bracket spacing)

### Key Dependencies
- **zigbee-herdsman**: 6.2.0 (exact version - critical for Zigbee protocol compatibility)
- **zigbee-herdsman-converters**: 25.42.0 (exact version - device definitions)
- **MQTT**: mqtt 5.14.1
- **Logging**: winston 3.18.3
- **YAML**: js-yaml 4.1.0
- **Decorators**: bind-decorator 1.0.11
- **WebSocket**: ws 8.18.1

### TypeScript Configuration
- **Strict Mode**: Enabled with `noImplicitAny` and `noImplicitThis`
- **Module System**: NodeNext with ESM interop
- **Decorators**: Experimental decorators enabled
- **Composite**: True (for project references)
- **Source Maps**: Inline source maps enabled
- **Output**: Compiled to `dist/` directory

## Project Architecture

### Directory Structure
```
lib/                    # Source TypeScript files
├── controller.ts       # Main controller orchestrating all components
├── mqtt.ts            # MQTT client management
├── zigbee.ts          # Zigbee network management
├── state.ts           # State management
├── eventBus.ts        # Event-driven communication
├── extension/         # Extension system (plugins)
│   ├── extension.ts   # Abstract base class
│   ├── availability.ts
│   ├── bind.ts
│   ├── bridge.ts
│   ├── configure.ts
│   └── ...
├── model/             # Domain models
│   ├── device.ts
│   └── group.ts
├── util/              # Utility functions
│   ├── logger.ts
│   ├── settings.ts
│   ├── utils.ts
│   └── ...
└── types/             # TypeScript type definitions
    └── api.ts
test/                  # Vitest test files
data/                  # Runtime configuration and data
```

### Architectural Patterns

#### Extension Pattern
All extensions inherit from the abstract `Extension` base class:
```typescript
abstract class Extension {
    protected zigbee: Zigbee;
    protected mqtt: Mqtt;
    protected state: State;
    protected publishEntityState: PublishEntityState;
    protected eventBus: EventBus;

    async start(): Promise<void> {}
    async stop(): Promise<void> {}
}
```

#### Event-Driven Architecture
Use the `EventBus` for component communication. Events are strongly typed:
```typescript
interface EventBusMap {
    deviceMessage: [data: eventdata.DeviceMessage];
    mqttMessage: [data: eventdata.MQTTMessage];
    publishEntityState: [data: eventdata.PublishEntityState];
    // ... other events
}
```

#### Dependency Injection
The `Controller` class instantiates and injects dependencies into extensions. Follow this pattern when creating new extensions.

## Code Style and Conventions

### Naming Conventions
- **Classes**: PascalCase (e.g., `Extension`, `Device`, `EventBus`)
- **Interfaces/Types**: PascalCase (e.g., `MqttPublishOptions`, `DeviceOptions`)
- **Functions/Methods**: camelCase (e.g., `publishEntityState`, `enableDisableExtension`)
- **Constants**: SCREAMING_SNAKE_CASE for top-level constants (e.g., `CURRENT_VERSION`, `LOG_LEVELS`)
- **Private members**: Prefix with underscore for private class fields only when needed to distinguish from public properties (e.g., `_definitionModelID`)
- **Files**: camelCase for TypeScript files (e.g., `eventBus.ts`, `externalJS.ts`)

### Import Organization
Follow this import order (separated by blank lines):
1. Node.js built-in modules (use `node:` prefix: `import fs from "node:fs"`)
2. Third-party libraries (e.g., `bind-decorator`, `mqtt`)
3. Type-only imports from external packages (using `type` keyword)
4. Internal absolute imports from project root
5. Type-only imports from internal modules

Example:
```typescript
import fs from "node:fs";
import bind from "bind-decorator";
import type {IClientOptions} from "mqtt";
import {connectAsync} from "mqtt";
import type {Zigbee2MQTTAPI} from "./types/api";
import logger from "./util/logger";
import * as settings from "./util/settings";
```

### Type Annotations
- Use `type` imports for TypeScript types: `import type * as zhc from "zigbee-herdsman-converters"`
- Explicitly type function parameters and return types
- Use `KeyValue` type for generic object payloads: `type KeyValue = Record<string, any>`
- Prefer interfaces for object shapes, type aliases for unions/intersections
- Use namespace exports for related types: `export type * as ZSpec from "zigbee-herdsman/dist/zspec"`

### Async/Await Patterns
- Always use `async/await` for asynchronous operations
- Return types should be explicitly `Promise<Type>`
- Methods that don't return values should be `Promise<void>`
- Use `Awaited<ReturnType<typeof fn>>` for inferring async function return types

### Decorators
Use `@bind` decorator from `bind-decorator` for methods that need `this` binding:
```typescript
@bind async onMQTTMessage(data: eventdata.MQTTMessage): Promise<void> {
    // Implementation
}
```

### Error Handling
- Use `throw new Error("message")` for explicit errors
- Include descriptive error messages
- Log errors using the logger: `logger.error("message")`
- For Zigbee-herdsman errors, log the stack trace: `logger.error((error as Error).stack!)`
- Catch and handle errors at appropriate boundaries (controller level)

### Logging
Use the centralized logger (winston-based):
```typescript
import logger from "./util/logger";

logger.info("message");
logger.warning("message");
logger.error("message");
logger.debug("message");
```

- Use namespaced loggers for specific modules (created internally by logger)
- Log levels: `error`, `warning`, `info`, `debug` (from most to least critical)
- Include relevant context in log messages (device names, IEEE addresses, etc.)

## Code Quality Standards

### Maintainability
- Write self-documenting code with clear, descriptive names
- Keep methods focused on single responsibilities
- Abstract classes should define clear contracts with protected members for subclasses
- Use constructor dependency injection for required dependencies
- Limit function complexity - methods should be concise and focused
- Use TypeScript's strict mode features (`noImplicitAny`, `noImplicitThis`)

### Performance
- Use `fs.rmSync(path, {recursive: true, force: true})` for synchronous file deletion when appropriate
- Leverage async/await for I/O operations to avoid blocking
- Use JSON stable stringify util for consistent object serialization
- Cache computed values when appropriate (see device model patterns)
- Use getter methods for computed properties that should be cached

### Security
- Validate input using Ajv JSON schema validation (see `settings.ts` pattern)
- Sanitize file paths using `path.join` from Node.js
- Use YAML safe loading: `yaml.safeLoad()`
- Handle sensitive data (credentials, tokens) through settings with proper defaults
- Never log sensitive information (passwords, tokens)

### Testability
- Write tests using Vitest with describe/it/expect patterns
- Mock external dependencies using Vitest's `vi.mock()`
- Use `beforeEach`, `afterEach`, `beforeAll`, `afterAll` for test setup/teardown
- Place test files in `test/` directory with `.test.ts` extension
- Mock constructors and modules in the pattern shown in `test/controller.test.ts`
- Use `flushPromises()` utility for async test synchronization
- Target 100% code coverage (configured in vitest.config.mts)

## Testing Standards

### Unit Testing Structure
```typescript
import {afterAll, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";

describe("ComponentName", () => {
    beforeEach(() => {
        // Setup
    });

    it("Should do something specific", async () => {
        // Arrange
        const input = {};

        // Act
        const result = await someFunction(input);

        // Assert
        expect(result).toBe(expected);
    });
});
```

### Mocking Patterns
- Create mock modules in `test/mocks/` directory
- Use `vi.fn()` for function mocks
- Use `vi.mock()` for module mocks
- Clear mocks in `afterEach` or between tests
- Mock external libraries like `mqtt`, `zigbee-herdsman` consistently

### Test Coverage
- All code in `lib/**` should be covered
- Use coverage reports: `pnpm test:coverage`
- Thresholds set to 100% (can be adjusted per project needs)
- Tests should cover both success and failure paths

## Documentation Standards

### JSDoc Comments
Use JSDoc-style comments for classes and public methods:
```typescript
/**
 * Besides initializing variables, the constructor should do nothing!
 *
 * @param {Zigbee} zigbee Zigbee controller
 * @param {Mqtt} mqtt MQTT controller
 * @param {State} state State controller
 * @param {Function} publishEntityState Method to publish device state to MQTT.
 * @param {EventBus} eventBus The event bus
 */
constructor(zigbee: Zigbee, mqtt: Mqtt, state: State, ...) {
```

### Comment Style
- Use single-line comments (`//`) for implementation notes
- Use JSDoc (`/** */`) for public APIs and class/method documentation
- Include context for non-obvious logic
- Document parameters with their types and purposes
- Use `@param` tags with TypeScript types in braces
- Use biome-ignore comments when necessary: `// biome-ignore lint/rule: reason`

### Code Documentation
- Document complex algorithms or business logic
- Explain "why" not just "what" when logic is non-trivial
- Include links to relevant issues or documentation when applicable
- Document deprecations and breaking changes

## TypeScript-Specific Guidelines

### Module System
- Use ES modules with `import`/`export` syntax
- Default exports for main classes: `export default class Device {}`
- Named exports for utilities and types: `export const LOG_LEVELS = ...`
- Namespace exports for related types: `export type * as ZSpec from ...`
- Use `.js` extension in imports for local modules when using dynamic imports: `await import("./extension/frontend.js")`

### Type Safety
- Enable all strict type checking options
- Use type guards and assertions when necessary: `asserts expose is zhc.Numeric`
- Prefer `unknown` over `any` when type is truly unknown
- Use `// biome-ignore lint/suspicious/noExplicitAny: API` when `any` is necessary
- Define proper interfaces for external module types (e.g., `unix-dgram.d.ts`)

### Generic Types
- Use generics for reusable, type-safe abstractions
- Example: `abstract class ExternalJSExtension<M> extends Extension`
- Constrain generics when appropriate
- Document generic type parameters

### Utility Types
- Use built-in utility types: `Partial`, `Required`, `Pick`, `Omit`, `Record`
- Use `Awaited<ReturnType<typeof fn>>` for async function return types
- Define custom utility types when patterns emerge
- Use `type` for aliases, `interface` for object shapes

## Version Control and Releases

### Versioning Strategy
- Follow Semantic Versioning (MAJOR.MINOR.PATCH)
- Current version managed in `package.json`
- Use `-dev` suffix for development versions (e.g., `2.6.2-dev`)
- Configuration version tracked separately: `CURRENT_VERSION = 4`

### Changelog
- Maintain CHANGELOG.md with all changes
- Group changes by type: Bug Fixes, Features, Breaking Changes
- Include issue/PR references: `([#28583](url))`
- Include commit references: `([09f33b3](url))`
- Use conventional commits format

### Git Workflow
- Development on `dev` branch
- Production releases from `master` branch
- Use meaningful commit messages
- Reference issues in commits

## Project-Specific Patterns

### Settings Management
- All configuration loaded through `util/settings.ts`
- Validate settings using Ajv with JSON schema
- Schema defined in `settings.schema.json`
- Support runtime setting changes with restart detection
- Use `settings.get()` to access current configuration
- Use `settings.getDevice(ieeeAddr)` for device-specific config

### Device and Group Models
- Devices and groups are domain models wrapping `zigbee-herdsman` entities
- Access underlying entity via `.zh` property
- Expose computed properties as getters
- Include definition from `zigbee-herdsman-converters`
- Handle coordinator devices specially (type checking)

### MQTT Integration
- MQTT client wrapped in `Mqtt` class
- Publish options: `retain`, `qos` properties
- Topics follow pattern: `{base_topic}/{device}/{attribute}`
- Event-based message handling via EventBus
- Clean disconnect handling with retry logic

### Extension System
- Extensions are loosely coupled plugins
- Lifecycle: constructor → start() → stop()
- Constructor should only assign properties (no side effects)
- Use EventBus for inter-extension communication
- Extensions can be enabled/disabled at runtime
- External extensions loaded from `data/external_extensions/`

### State Management
- State persisted to `state.json`
- Cached in memory for performance
- Device states include all exposed attributes
- State changes trigger events via EventBus

## Best Practices Specific to This Project

1. **Never use language features beyond TypeScript 5.9.3 or ES2024**
2. **Always respect exact versions of zigbee-herdsman and zigbee-herdsman-converters** - these are critical for device compatibility
3. **Use the EventBus for all component communication** - avoid direct coupling
4. **Follow the Extension pattern for new features** - don't add logic directly to Controller
5. **Log appropriately** - info for user-relevant events, debug for developer info, error for failures
6. **Test with real Zigbee scenarios** - many edge cases exist with different device types
7. **Handle coordinator specially** - coordinator is a device but with unique behavior
8. **Validate all external input** - MQTT messages, configuration files, device data
9. **Use the bind decorator** for event handlers to preserve `this` context
10. **Match the exact code formatting** - Biome enforces 4 spaces, 150 line width, no bracket spacing

## Common Patterns to Follow

### Creating a New Extension
1. Extend `Extension` abstract class
2. Accept all dependencies in constructor
3. Implement `start()` method for initialization
4. Subscribe to EventBus events in `start()`
5. Implement `stop()` method for cleanup
6. Export as default: `export default class MyExtension extends Extension`

### Accessing Device Information
```typescript
const device: Device; // Our wrapper
device.ieeeAddr;      // IEEE address
device.name;          // Friendly name
device.zh;            // Underlying zigbee-herdsman device
device.definition;    // zigbee-herdsman-converters definition
device.options;       // User configuration
```

### Publishing MQTT Messages
```typescript
await this.mqtt.publish(topic, message, {retain: true, qos: 0});
```

### Emitting Events
```typescript
this.eventBus.emit('deviceMessage', {device, message});
```

### Listening to Events
```typescript
this.eventBus.on('deviceMessage', this.onDeviceMessage, this);
```

## Integration Points

### Zigbee-Herdsman Integration
- Start controller: `await this.zigbee.start()`
- Access coordinator: `this.zigbee.coordinator()`
- Device operations through `zigbee-herdsman` API
- Event handling through EventBus wrappers

### MQTT Integration
- Connect: `await this.mqtt.connect()`
- Subscribe: `await this.mqtt.subscribe(topic)`
- Publish: `await this.mqtt.publish(topic, message, options)`
- Handle messages via EventBus `mqttMessage` event

### Frontend Integration
- Optional extension loaded dynamically
- Serves static files with compression
- WebSocket support for real-time updates
- Configurable port and base URL

### Home Assistant Integration
- Optional extension for discovery
- Publishes discovery messages to MQTT
- Supports entities, sensors, and devices
- Configurable discovery topic

## Critical Compatibility Notes

1. **Node.js**: Only versions 20, 22, and 24 are supported
2. **TypeScript**: Features must be compatible with 5.9.3
3. **Zigbee Libraries**: Exact versions are critical - do not suggest upgrades without testing
4. **MQTT Protocol**: Uses MQTT 3.1.1 and 5.0 features
5. **ES Modules**: Project uses ESM with NodeNext resolution
6. **Experimental Decorators**: Required for `@bind` decorator support

## When in Doubt

1. **Search for similar patterns** in the existing codebase
2. **Check existing extensions** for implementation examples
3. **Follow the controller and extension architecture** - don't bypass it
4. **Consult the test files** for usage examples
5. **Match the exact style** - run `pnpm check` to verify
6. **Prioritize consistency** over external best practices
7. **Test thoroughly** - this project controls real hardware

## Resources

- Repository: https://github.com/Koenkk/zigbee2mqtt
- Documentation: https://koenkk.github.io/zigbee2mqtt
- License: GPL-3.0
- Issue Tracker: https://github.com/Koenkk/zigbee2mqtt/issues
