iobroker.emby 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,880 @@
1
+ # ioBroker Adapter Development with GitHub Copilot
2
+
3
+ **Version:** 0.5.7
4
+ **Template Source:** https://github.com/DrozmotiX/ioBroker-Copilot-Instructions
5
+
6
+ This file contains instructions and best practices for GitHub Copilot when working on ioBroker adapter development.
7
+
8
+ ---
9
+
10
+ ## 📑 Table of Contents
11
+
12
+ 1. [Project Context](#project-context)
13
+ 2. [Code Quality & Standards](#code-quality--standards)
14
+ - [Code Style Guidelines](#code-style-guidelines)
15
+ - [ESLint Configuration](#eslint-configuration)
16
+ 3. [Testing](#testing)
17
+ - [Unit Testing](#unit-testing)
18
+ - [Integration Testing](#integration-testing)
19
+ - [API Testing with Credentials](#api-testing-with-credentials)
20
+ 4. [Development Best Practices](#development-best-practices)
21
+ - [Dependency Management](#dependency-management)
22
+ - [HTTP Client Libraries](#http-client-libraries)
23
+ - [Error Handling](#error-handling)
24
+ 5. [Admin UI Configuration](#admin-ui-configuration)
25
+ - [JSON-Config Setup](#json-config-setup)
26
+ - [Translation Management](#translation-management)
27
+ 6. [Documentation](#documentation)
28
+ - [README Updates](#readme-updates)
29
+ - [Changelog Management](#changelog-management)
30
+ 7. [CI/CD & GitHub Actions](#cicd--github-actions)
31
+ - [Workflow Configuration](#workflow-configuration)
32
+ - [Testing Integration](#testing-integration)
33
+
34
+ ---
35
+
36
+ ## Project Context
37
+
38
+ You are working on an ioBroker adapter. ioBroker is an integration platform for the Internet of Things, focused on building smart home and industrial IoT solutions. Adapters are plugins that connect ioBroker to external systems, devices, or services.
39
+
40
+ **Adapter-Specific Context - Emby Media Server Integration:**
41
+ - **Adapter Name:** emby
42
+ - **Primary Function:** Connect to and control Emby Media Server instances via WebSocket API
43
+ - **Key Technologies:** WebSocket (websocket npm package), HTTP REST API (request npm package)
44
+ - **External Service:** Emby Media Server - personal media server for organizing and streaming movies, TV shows, music, and photos
45
+ - **Connection Type:** Network-based connection using IP/port configuration with SSL support
46
+ - **Authentication:** API key-based authentication with Emby server
47
+ - **Key Features:** Media playback control, device detection and filtering, session monitoring, real-time status updates
48
+ - **Configuration Options:** Server IP/port, SSL toggle, API key, device ID filtering, timeout settings for episode transitions
49
+
50
+ ---
51
+
52
+ ## Code Quality & Standards
53
+
54
+ ### Code Style Guidelines
55
+
56
+ - Follow JavaScript/TypeScript best practices
57
+ - Use async/await for asynchronous operations
58
+ - Implement proper resource cleanup in `unload()` method
59
+ - Use semantic versioning for adapter releases
60
+ - Include proper JSDoc comments for public methods
61
+
62
+ **Timer and Resource Cleanup Example:**
63
+ ```javascript
64
+ private connectionTimer?: NodeJS.Timeout;
65
+
66
+ async onReady() {
67
+ this.connectionTimer = setInterval(() => this.checkConnection(), 30000);
68
+ }
69
+
70
+ onUnload(callback) {
71
+ try {
72
+ if (this.connectionTimer) {
73
+ clearInterval(this.connectionTimer);
74
+ this.connectionTimer = undefined;
75
+ }
76
+ callback();
77
+ } catch (e) {
78
+ callback();
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### ESLint Configuration
84
+
85
+ **CRITICAL:** ESLint validation must run FIRST in your CI/CD pipeline, before any other tests. This "lint-first" approach catches code quality issues early.
86
+
87
+ #### Setup
88
+ ```bash
89
+ npm install --save-dev eslint @iobroker/eslint-config
90
+ ```
91
+
92
+ #### Configuration (.eslintrc.json)
93
+ ```json
94
+ {
95
+ "extends": "@iobroker/eslint-config",
96
+ "rules": {
97
+ // Add project-specific rule overrides here if needed
98
+ }
99
+ }
100
+ ```
101
+
102
+ #### Package.json Scripts
103
+ ```json
104
+ {
105
+ "scripts": {
106
+ "lint": "eslint --max-warnings 0 .",
107
+ "lint:fix": "eslint . --fix"
108
+ }
109
+ }
110
+ ```
111
+
112
+ #### Best Practices
113
+ 1. ✅ Run ESLint before committing — fix ALL warnings, not just errors
114
+ 2. ✅ Use `lint:fix` for auto-fixable issues
115
+ 3. ✅ Don't disable rules without documentation
116
+ 4. ✅ Lint all relevant files (main code, tests, build scripts)
117
+ 5. ✅ Keep `@iobroker/eslint-config` up to date
118
+ 6. ✅ **ESLint warnings are treated as errors in CI** (`--max-warnings 0`). The `lint` script above already includes this flag — run `npm run lint` to match CI behavior locally
119
+
120
+ #### Common Issues
121
+ - **Unused variables**: Remove or prefix with underscore (`_variable`)
122
+ - **Missing semicolons**: Run `npm run lint:fix`
123
+ - **Indentation**: Use 4 spaces (ioBroker standard)
124
+ - **console.log**: Replace with `adapter.log.debug()` or remove
125
+
126
+ ---
127
+
128
+ ## Testing
129
+
130
+ ### Unit Testing
131
+
132
+ - Use Jest as the primary testing framework
133
+ - Create tests for all adapter main functions and helper methods
134
+ - Test error handling scenarios and edge cases
135
+ - Mock external API calls and hardware dependencies
136
+ - For adapters connecting to APIs/devices not reachable by internet, provide example data files
137
+
138
+ **Example Structure:**
139
+ ```javascript
140
+ describe('AdapterName', () => {
141
+ let adapter;
142
+
143
+ beforeEach(() => {
144
+ // Setup test adapter instance
145
+ });
146
+
147
+ test('should initialize correctly', () => {
148
+ // Test adapter initialization
149
+ });
150
+ });
151
+ ```
152
+
153
+ ### Integration Testing
154
+
155
+ **CRITICAL:** Use the official `@iobroker/testing` framework. This is the ONLY correct way to test ioBroker adapters.
156
+
157
+ **Official Documentation:** https://github.com/ioBroker/testing
158
+
159
+ #### Framework Structure
160
+
161
+ **✅ Correct Pattern:**
162
+ ```javascript
163
+ const path = require('path');
164
+ const { tests } = require('@iobroker/testing');
165
+
166
+ tests.integration(path.join(__dirname, '..'), {
167
+ defineAdditionalTests({ suite }) {
168
+ suite('Test adapter with specific configuration', (getHarness) => {
169
+ let harness;
170
+
171
+ before(() => {
172
+ harness = getHarness();
173
+ });
174
+
175
+ it('should configure and start adapter', function () {
176
+ return new Promise(async (resolve, reject) => {
177
+ try {
178
+ // Get adapter object
179
+ const obj = await new Promise((res, rej) => {
180
+ harness.objects.getObject('system.adapter.your-adapter.0', (err, o) => {
181
+ if (err) return rej(err);
182
+ res(o);
183
+ });
184
+ });
185
+
186
+ if (!obj) return reject(new Error('Adapter object not found'));
187
+
188
+ // Configure adapter
189
+ Object.assign(obj.native, {
190
+ position: '52.520008,13.404954',
191
+ createHourly: true,
192
+ });
193
+
194
+ harness.objects.setObject(obj._id, obj);
195
+
196
+ // Start and wait
197
+ await harness.startAdapterAndWait();
198
+ await new Promise(resolve => setTimeout(resolve, 15000));
199
+
200
+ // Verify states
201
+ const stateIds = await harness.dbConnection.getStateIDs('your-adapter.0.*');
202
+
203
+ if (stateIds.length > 0) {
204
+ console.log('✅ Adapter successfully created states');
205
+ await harness.stopAdapter();
206
+ resolve(true);
207
+ } else {
208
+ reject(new Error('Adapter did not create any states'));
209
+ }
210
+ } catch (error) {
211
+ reject(error);
212
+ }
213
+ });
214
+ }).timeout(40000);
215
+ });
216
+ }
217
+ });
218
+ ```
219
+
220
+ #### Testing Success AND Failure Scenarios
221
+
222
+ **IMPORTANT:** For every "it works" test, implement corresponding "it fails gracefully" tests.
223
+
224
+ **Failure Scenario Example:**
225
+ ```javascript
226
+ it('should NOT create daily states when daily is disabled', function () {
227
+ return new Promise(async (resolve, reject) => {
228
+ try {
229
+ harness = getHarness();
230
+ const obj = await new Promise((res, rej) => {
231
+ harness.objects.getObject('system.adapter.your-adapter.0', (err, o) => {
232
+ if (err) return rej(err);
233
+ res(o);
234
+ });
235
+ });
236
+
237
+ if (!obj) return reject(new Error('Adapter object not found'));
238
+
239
+ Object.assign(obj.native, {
240
+ createDaily: false, // Daily disabled
241
+ });
242
+
243
+ await new Promise((res, rej) => {
244
+ harness.objects.setObject(obj._id, obj, (err) => {
245
+ if (err) return rej(err);
246
+ res(undefined);
247
+ });
248
+ });
249
+
250
+ await harness.startAdapterAndWait();
251
+ await new Promise((res) => setTimeout(res, 20000));
252
+
253
+ const stateIds = await harness.dbConnection.getStateIDs('your-adapter.0.*');
254
+ const dailyStates = stateIds.filter((key) => key.includes('daily'));
255
+
256
+ if (dailyStates.length === 0) {
257
+ console.log('✅ No daily states found as expected');
258
+ resolve(true);
259
+ } else {
260
+ reject(new Error('Expected no daily states but found some'));
261
+ }
262
+
263
+ await harness.stopAdapter();
264
+ } catch (error) {
265
+ reject(error);
266
+ }
267
+ });
268
+ }).timeout(40000);
269
+ ```
270
+
271
+ #### Key Rules
272
+
273
+ 1. ✅ Use `@iobroker/testing` framework
274
+ 2. ✅ Configure via `harness.objects.setObject()`
275
+ 3. ✅ Start via `harness.startAdapterAndWait()`
276
+ 4. ✅ Verify states via `harness.states.getState()`
277
+ 5. ✅ Allow proper timeouts for async operations
278
+ 6. ❌ NEVER test API URLs directly
279
+ 7. ❌ NEVER bypass the harness system
280
+
281
+ #### Workflow Dependencies
282
+
283
+ Integration tests should run ONLY after lint and adapter tests pass:
284
+
285
+ ```yaml
286
+ integration-tests:
287
+ needs: [check-and-lint, adapter-tests]
288
+ runs-on: ubuntu-22.04
289
+ ```
290
+
291
+ ### API Testing with Credentials
292
+
293
+ For adapters connecting to external APIs requiring authentication:
294
+
295
+ #### Password Encryption for Integration Tests
296
+
297
+ ```javascript
298
+ async function encryptPassword(harness, password) {
299
+ const systemConfig = await harness.objects.getObjectAsync("system.config");
300
+ if (!systemConfig?.native?.secret) {
301
+ throw new Error("Could not retrieve system secret for password encryption");
302
+ }
303
+
304
+ const secret = systemConfig.native.secret;
305
+ let result = '';
306
+ for (let i = 0; i < password.length; ++i) {
307
+ result += String.fromCharCode(secret[i % secret.length].charCodeAt(0) ^ password.charCodeAt(i));
308
+ }
309
+ return result;
310
+ }
311
+ ```
312
+
313
+ #### Demo Credentials Testing Pattern
314
+
315
+ - Use provider demo credentials when available (e.g., `demo@api-provider.com` / `demo`)
316
+ - Create separate test file: `test/integration-demo.js`
317
+ - Add npm script: `"test:integration-demo": "mocha test/integration-demo --exit"`
318
+ - Implement clear success/failure criteria
319
+
320
+ **Example Implementation:**
321
+ ```javascript
322
+ it("Should connect to API with demo credentials", async () => {
323
+ const encryptedPassword = await encryptPassword(harness, "demo_password");
324
+
325
+ await harness.changeAdapterConfig("your-adapter", {
326
+ native: {
327
+ username: "demo@provider.com",
328
+ password: encryptedPassword,
329
+ }
330
+ });
331
+
332
+ await harness.startAdapter();
333
+ await new Promise(resolve => setTimeout(resolve, 60000));
334
+
335
+ const connectionState = await harness.states.getStateAsync("your-adapter.0.info.connection");
336
+
337
+ if (connectionState?.val === true) {
338
+ console.log("✅ SUCCESS: API connection established");
339
+ return true;
340
+ } else {
341
+ throw new Error("API Test Failed: Expected API connection. Check logs for API errors.");
342
+ }
343
+ }).timeout(120000);
344
+ ```
345
+
346
+ ---
347
+
348
+ ## Development Best Practices
349
+
350
+ ### Dependency Management
351
+
352
+ - Always use `npm` for dependency management
353
+ - Use `npm ci` for installing existing dependencies (respects package-lock.json)
354
+ - Use `npm install` only when adding or updating dependencies
355
+ - Keep dependencies minimal and focused
356
+ - Only update dependencies in separate Pull Requests
357
+
358
+ **When modifying package.json:**
359
+ 1. Run `npm install` to sync package-lock.json
360
+ 2. Commit both package.json and package-lock.json together
361
+
362
+ **Best Practices:**
363
+ - Prefer built-in Node.js modules when possible
364
+ - Use `@iobroker/adapter-core` for adapter base functionality
365
+ - Avoid deprecated packages
366
+ - Document specific version requirements
367
+
368
+ ### HTTP Client Libraries
369
+
370
+ - **Preferred:** Use native `fetch` API (Node.js 20+ required)
371
+ - **Avoid:** `axios` unless specific features are required
372
+
373
+ **Example with fetch:**
374
+ ```javascript
375
+ try {
376
+ const response = await fetch('https://api.example.com/data');
377
+ if (!response.ok) {
378
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
379
+ }
380
+ const data = await response.json();
381
+ } catch (error) {
382
+ this.log.error(`API request failed: ${error.message}`);
383
+ }
384
+ ```
385
+
386
+ **Other Recommendations:**
387
+ - **Logging:** Use adapter built-in logging (`this.log.*`)
388
+ - **Scheduling:** Use adapter built-in timers and intervals
389
+ - **File operations:** Use Node.js `fs/promises`
390
+ - **Configuration:** Use adapter config system
391
+
392
+ ### Error Handling
393
+
394
+ - Always catch and log errors appropriately
395
+ - Use adapter log levels (error, warn, info, debug)
396
+ - Provide meaningful, user-friendly error messages
397
+ - Handle network failures gracefully
398
+ - Implement retry mechanisms where appropriate
399
+ - Always clean up timers, intervals, and resources in `unload()` method
400
+
401
+ **Example:**
402
+ ```javascript
403
+ try {
404
+ await this.connectToDevice();
405
+ } catch (error) {
406
+ this.log.error(`Failed to connect to device: ${error.message}`);
407
+ this.setState('info.connection', false, true);
408
+ // Implement retry logic if needed
409
+ }
410
+ ```
411
+
412
+ ---
413
+
414
+ ## Admin UI Configuration
415
+
416
+ ### JSON-Config Setup
417
+
418
+ Use JSON-Config format for modern ioBroker admin interfaces.
419
+
420
+ **Example Structure:**
421
+ ```json
422
+ {
423
+ "type": "panel",
424
+ "items": {
425
+ "host": {
426
+ "type": "text",
427
+ "label": "Host address",
428
+ "help": "IP address or hostname of the device"
429
+ }
430
+ }
431
+ }
432
+ ```
433
+
434
+ **Guidelines:**
435
+ - ✅ Use consistent naming conventions
436
+ - ✅ Provide sensible default values
437
+ - ✅ Include validation for required fields
438
+ - ✅ Add tooltips for complex options
439
+ - ✅ Ensure translations for all supported languages (minimum English and German)
440
+ - ✅ Write end-user friendly labels, avoid technical jargon
441
+
442
+ ### Translation Management
443
+
444
+ **CRITICAL:** Translation files must stay synchronized with `admin/jsonConfig.json`. Orphaned keys or missing translations cause UI issues and PR review delays.
445
+
446
+ #### Overview
447
+ - **Location:** `admin/i18n/{lang}/translations.json` for 11 languages (de, en, es, fr, it, nl, pl, pt, ru, uk, zh-cn)
448
+ - **Source of truth:** `admin/jsonConfig.json` - all `label` and `help` properties must have translations
449
+ - **Command:** `npm run translate` - auto-generates translations but does NOT remove orphaned keys
450
+ - **Formatting:** English uses tabs, other languages use 4 spaces
451
+
452
+ #### Critical Rules
453
+ 1. ✅ Keys must match exactly with jsonConfig.json
454
+ 2. ✅ No orphaned keys in translation files
455
+ 3. ✅ All translations must be in native language (no English fallbacks)
456
+ 4. ✅ Keys must be sorted alphabetically
457
+
458
+ #### Workflow for Translation Updates
459
+
460
+ **When modifying admin/jsonConfig.json:**
461
+
462
+ 1. Make your changes to labels/help texts
463
+ 2. Run automatic translation: `npm run translate`
464
+ 3. Create validation script (`scripts/validate-translations.js`):
465
+
466
+ ```javascript
467
+ const fs = require('fs');
468
+ const path = require('path');
469
+ const jsonConfig = JSON.parse(fs.readFileSync('admin/jsonConfig.json', 'utf8'));
470
+
471
+ function extractTexts(obj, texts = new Set()) {
472
+ if (typeof obj === 'object' && obj !== null) {
473
+ if (obj.label) texts.add(obj.label);
474
+ if (obj.help) texts.add(obj.help);
475
+ for (const key in obj) {
476
+ extractTexts(obj[key], texts);
477
+ }
478
+ }
479
+ return texts;
480
+ }
481
+
482
+ const requiredTexts = extractTexts(jsonConfig);
483
+ const languages = ['de', 'en', 'es', 'fr', 'it', 'nl', 'pl', 'pt', 'ru', 'uk', 'zh-cn'];
484
+ let hasErrors = false;
485
+
486
+ languages.forEach(lang => {
487
+ const translationPath = path.join('admin', 'i18n', lang, 'translations.json');
488
+ const translations = JSON.parse(fs.readFileSync(translationPath, 'utf8'));
489
+ const translationKeys = new Set(Object.keys(translations));
490
+
491
+ const missing = Array.from(requiredTexts).filter(text => !translationKeys.has(text));
492
+ const orphaned = Array.from(translationKeys).filter(key => !requiredTexts.has(key));
493
+
494
+ console.log(`\n=== ${lang} ===`);
495
+ if (missing.length > 0) {
496
+ console.error('❌ Missing keys:', missing);
497
+ hasErrors = true;
498
+ }
499
+ if (orphaned.length > 0) {
500
+ console.error('❌ Orphaned keys (REMOVE THESE):', orphaned);
501
+ hasErrors = true;
502
+ }
503
+ if (missing.length === 0 && orphaned.length === 0) {
504
+ console.log('✅ All keys match!');
505
+ }
506
+ });
507
+
508
+ process.exit(hasErrors ? 1 : 0);
509
+ ```
510
+
511
+ 4. Run validation: `node scripts/validate-translations.js`
512
+ 5. Remove orphaned keys manually from all translation files
513
+ 6. Add missing translations in native languages
514
+ 7. Run: `npm run lint && npm run test`
515
+
516
+ #### Add Validation to package.json
517
+
518
+ ```json
519
+ {
520
+ "scripts": {
521
+ "translate": "translate-adapter",
522
+ "validate:translations": "node scripts/validate-translations.js",
523
+ "pretest": "npm run lint && npm run validate:translations"
524
+ }
525
+ }
526
+ ```
527
+
528
+ #### Translation Checklist
529
+
530
+ Before committing changes to admin UI or translations:
531
+ 1. ✅ Validation script shows "All keys match!" for all 11 languages
532
+ 2. ✅ No orphaned keys in any translation file
533
+ 3. ✅ All translations in native language
534
+ 4. ✅ Keys alphabetically sorted
535
+ 5. ✅ `npm run lint` passes
536
+ 6. ✅ `npm run test` passes
537
+ 7. ✅ Admin UI displays correctly
538
+
539
+ ---
540
+
541
+ ## Documentation
542
+
543
+ ### README Updates
544
+
545
+ #### Required Sections
546
+ 1. **Installation** - Clear npm/ioBroker admin installation steps
547
+ 2. **Configuration** - Detailed configuration options with examples
548
+ 3. **Usage** - Practical examples and use cases
549
+ 4. **Changelog** - Version history (use "## **WORK IN PROGRESS**" for ongoing changes)
550
+ 5. **License** - License information (typically MIT for ioBroker adapters)
551
+ 6. **Support** - Links to issues, discussions, community support
552
+
553
+ #### Documentation Standards
554
+ - Use clear, concise language
555
+ - Include code examples for configuration
556
+ - Add screenshots for admin interface when applicable
557
+ - Maintain multilingual support (minimum English and German)
558
+ - Always reference issues in commits and PRs (e.g., "fixes #xx")
559
+
560
+ #### Mandatory README Updates for PRs
561
+
562
+ For **every PR or new feature**, always add a user-friendly entry to README.md:
563
+
564
+ - Add entries under `## **WORK IN PROGRESS**` section
565
+ - Use format: `* (author) **TYPE**: Description of user-visible change`
566
+ - Types: **NEW** (features), **FIXED** (bugs), **ENHANCED** (improvements), **TESTING** (test additions), **CI/CD** (automation)
567
+ - Focus on user impact, not technical details
568
+
569
+ **Example:**
570
+ ```markdown
571
+ ## **WORK IN PROGRESS**
572
+
573
+ * (DutchmanNL) **FIXED**: Adapter now properly validates login credentials (fixes #25)
574
+ * (DutchmanNL) **NEW**: Added device discovery to simplify initial setup
575
+ ```
576
+
577
+ ### Changelog Management
578
+
579
+ Follow the [AlCalzone release-script](https://github.com/AlCalzone/release-script) standard.
580
+
581
+ #### Format Requirements
582
+
583
+ ```markdown
584
+ # Changelog
585
+
586
+ <!--
587
+ Placeholder for the next version (at the beginning of the line):
588
+ ## **WORK IN PROGRESS**
589
+ -->
590
+
591
+ ## **WORK IN PROGRESS**
592
+
593
+ - (author) **NEW**: Added new feature X
594
+ - (author) **FIXED**: Fixed bug Y (fixes #25)
595
+
596
+ ## v0.1.0 (2023-01-01)
597
+ Initial release
598
+ ```
599
+
600
+ #### Workflow Process
601
+ - **During Development:** All changes go under `## **WORK IN PROGRESS**`
602
+ - **For Every PR:** Add user-facing changes to WORK IN PROGRESS section
603
+ - **Before Merge:** Version number and date added when merging to main
604
+ - **Release Process:** Release-script automatically converts placeholder to actual version
605
+
606
+ #### Change Entry Format
607
+ - Format: `- (author) **TYPE**: User-friendly description`
608
+ - Types: **NEW**, **FIXED**, **ENHANCED**
609
+ - Focus on user impact, not technical implementation
610
+ - Reference issues: "fixes #XX" or "solves #XX"
611
+
612
+ ---
613
+
614
+ ## CI/CD & GitHub Actions
615
+
616
+ ### Workflow Configuration
617
+
618
+ #### GitHub Actions Best Practices
619
+
620
+ **Must use ioBroker official testing actions:**
621
+ - `ioBroker/testing-action-check@v1` for lint and package validation
622
+ - `ioBroker/testing-action-adapter@v1` for adapter tests
623
+ - `ioBroker/testing-action-deploy@v1` for automated releases with Trusted Publishing (OIDC)
624
+
625
+ **Configuration:**
626
+ - **Node.js versions:** Test on 20.x, 22.x, 24.x
627
+ - **Platform:** Use ubuntu-22.04
628
+ - **Automated releases:** Deploy to npm on version tags (requires NPM Trusted Publishing)
629
+ - **Monitoring:** Include Sentry release tracking for error monitoring
630
+
631
+ #### Critical: Lint-First Validation Workflow
632
+
633
+ **ALWAYS run ESLint checks BEFORE other tests.** Benefits:
634
+ - Catches code quality issues immediately
635
+ - Prevents wasting CI resources on tests that would fail due to linting errors
636
+ - Provides faster feedback to developers
637
+ - Enforces consistent code quality
638
+
639
+ **Workflow Dependency Configuration:**
640
+ ```yaml
641
+ jobs:
642
+ check-and-lint:
643
+ # Runs ESLint and package validation
644
+ # Uses: ioBroker/testing-action-check@v1
645
+
646
+ adapter-tests:
647
+ needs: [check-and-lint] # Wait for linting to pass
648
+ # Run adapter unit tests
649
+
650
+ integration-tests:
651
+ needs: [check-and-lint, adapter-tests] # Wait for both
652
+ # Run integration tests
653
+ ```
654
+
655
+ **Key Points:**
656
+ - The `check-and-lint` job has NO dependencies - runs first
657
+ - ALL other test jobs MUST list `check-and-lint` in their `needs` array
658
+ - If linting fails, no other tests run, saving time
659
+ - Fix all ESLint errors before proceeding
660
+
661
+ ### Testing Integration
662
+
663
+ #### API Testing in CI/CD
664
+
665
+ For adapters with external API dependencies:
666
+
667
+ ```yaml
668
+ demo-api-tests:
669
+ if: contains(github.event.head_commit.message, '[skip ci]') == false
670
+ runs-on: ubuntu-22.04
671
+
672
+ steps:
673
+ - name: Checkout code
674
+ uses: actions/checkout@v4
675
+
676
+ - name: Use Node.js 20.x
677
+ uses: actions/setup-node@v4
678
+ with:
679
+ node-version: 20.x
680
+ cache: 'npm'
681
+
682
+ - name: Install dependencies
683
+ run: npm ci
684
+
685
+ - name: Run demo API tests
686
+ run: npm run test:integration-demo
687
+ ```
688
+
689
+ #### Testing Best Practices
690
+ - Run credential tests separately from main test suite
691
+ - Don't make credential tests required for deployment
692
+ - Provide clear failure messages for API issues
693
+ - Use appropriate timeouts for external calls (120+ seconds)
694
+
695
+ #### Package.json Integration
696
+ ```json
697
+ {
698
+ "scripts": {
699
+ "test:integration-demo": "mocha test/integration-demo --exit"
700
+ }
701
+ }
702
+ ```
703
+
704
+ ---
705
+
706
+ ## Adapter Core Integration
707
+
708
+ ```javascript
709
+ const utils = require('@iobroker/adapter-core');
710
+ let adapter;
711
+
712
+ function startAdapter(options) {
713
+ options = options || {};
714
+ Object.assign(options, {
715
+ name: 'emby', // Adapter name
716
+ stateChange: onStateChange,
717
+ unload: onUnload,
718
+ ready: onReady,
719
+ });
720
+ adapter = new utils.Adapter(options);
721
+ return adapter;
722
+ }
723
+ ```
724
+
725
+ ### State Management
726
+ - Create states using `adapter.setObjectNotExists()`
727
+ - Update states with `adapter.setState(id, value, ack)`
728
+ - Subscribe to state changes with `adapter.subscribeStates('*')`
729
+ - Use appropriate state roles (media.*, button, indicator.connected, etc.)
730
+
731
+ ### Configuration Access
732
+ - Access configuration via `adapter.config.propertyName`
733
+ - Validate configuration values before use
734
+ - Handle missing or invalid configuration gracefully
735
+
736
+ ### Logging Best Practices
737
+ ```javascript
738
+ adapter.log.debug('Detailed debugging information');
739
+ adapter.log.info('General information');
740
+ adapter.log.warn('Warning messages');
741
+ adapter.log.error('Error messages with context');
742
+ ```
743
+
744
+ ### Resource Cleanup (Emby-specific)
745
+ ```javascript
746
+ function onUnload(callback) {
747
+ try {
748
+ // Close WebSocket connection
749
+ if (connection) {
750
+ connection.send('{"MessageType":"SessionsStop", "Data": ""}');
751
+ connection.close();
752
+ }
753
+
754
+ // Clear all timeouts
755
+ Object.keys(timeouts).forEach((timeout) => {
756
+ clearTimeout(timeouts[timeout]);
757
+ });
758
+
759
+ adapter.log.info('cleaned everything up...');
760
+ callback();
761
+ } catch (e) {
762
+ adapter.log.error('Error during cleanup: ' + e.message);
763
+ callback();
764
+ }
765
+ }
766
+ ```
767
+
768
+ ## API Integration Patterns
769
+
770
+ ### WebSocket Connection Management
771
+ ```javascript
772
+ const W3CWebSocket = require('websocket').w3cwebsocket;
773
+
774
+ function connectWebSocket() {
775
+ const protocol = adapter.config.isSSL ? 'wss' : 'ws';
776
+ const url = `${protocol}://${adapter.config.ip}/embywebsocket?api_key=${adapter.config.apikey}`;
777
+
778
+ connection = new W3CWebSocket(url);
779
+ connection.onopen = handleWebSocketOpen;
780
+ connection.onmessage = handleWebSocketMessage;
781
+ connection.onerror = handleWebSocketError;
782
+ connection.onclose = handleWebSocketClose;
783
+ }
784
+ ```
785
+
786
+ ### HTTP Request Patterns
787
+ ```javascript
788
+ const request = require('request');
789
+
790
+ function makeApiRequest(endpoint, callback) {
791
+ const protocol = adapter.config.isSSL ? 'https' : 'http';
792
+ const url = `${protocol}://${adapter.config.ip}/emby/${endpoint}?api_key=${adapter.config.apikey}`;
793
+
794
+ request({ url: url, json: true }, (error, response, body) => {
795
+ if (error) {
796
+ adapter.log.error('API request failed: ' + error.message);
797
+ return callback(error);
798
+ }
799
+ callback(null, body);
800
+ });
801
+ }
802
+ ```
803
+
804
+ ### Device Management
805
+ - Filter devices based on configuration (`adapter.config.deviceIds`)
806
+ - Create device channels and states dynamically
807
+ - Handle device state changes and media information updates
808
+ - Implement device-specific command handling
809
+
810
+ ## File Organization
811
+
812
+ ### Main Files
813
+ - `main.js` - Primary adapter logic and WebSocket handling
814
+ - `io-package.json` - Adapter metadata and configuration schema
815
+ - `package.json` - Node.js package configuration and dependencies
816
+
817
+ ### Admin Interface
818
+ - `admin/index_m.html` - Configuration interface (Materialize UI)
819
+ - `admin/words.js` - Internationalization strings
820
+ - `admin/emby.png` - Adapter icon
821
+
822
+ ### Testing Structure
823
+ - `test/integration.js` - Integration tests using @iobroker/testing
824
+ - `test/package.js` - Package validation tests
825
+ - Additional test files should follow `*.test.js` naming
826
+
827
+ ## Dependencies and Libraries
828
+
829
+ ### Core Dependencies
830
+ - `@iobroker/adapter-core` - ioBroker adapter framework
831
+ - `websocket` - WebSocket client for Emby server communication
832
+ - `request` - HTTP client for REST API calls (consider upgrading to axios or fetch)
833
+
834
+ ### Development Dependencies
835
+ - `@iobroker/testing` - Testing framework for ioBroker adapters
836
+ - `eslint` + `prettier` - Code quality and formatting
837
+ - `mocha` - Test runner
838
+ - `typescript` - Type checking support
839
+
840
+ ### Security Considerations
841
+ - Validate and sanitize all user inputs
842
+ - Use secure WebSocket connections (WSS) when available
843
+ - Store API keys securely in adapter configuration
844
+ - Implement rate limiting for API requests when appropriate
845
+
846
+ ## Performance Optimization
847
+
848
+ ### Timeout Management
849
+ - Use appropriate timeout values for different operations
850
+ - Clear timeouts properly to prevent memory leaks
851
+ - Implement timeout for episode transitions (`adapter.config.timeout`)
852
+
853
+ ### State Updates
854
+ - Only update states when values actually change
855
+ - Use acknowledgment flags appropriately (`ack: true/false`)
856
+ - Batch state updates when possible to reduce overhead
857
+
858
+ ### Memory Management
859
+ - Clean up event listeners and timers in unload function
860
+ - Avoid memory leaks in long-running WebSocket connections
861
+ - Monitor and handle large data structures efficiently
862
+
863
+ ## Media Server Specific Patterns
864
+
865
+ ### Session Handling
866
+ - Track active sessions and their states
867
+ - Handle session start/stop events
868
+ - Update media information based on session data
869
+
870
+ ### Playback Control
871
+ - Implement standard media control commands (play, pause, stop, etc.)
872
+ - Handle volume control and mute functionality
873
+ - Support seeking and navigation commands
874
+
875
+ ### Media Information Display
876
+ - Extract and display currently playing media details
877
+ - Show progress information and remaining time
878
+ - Handle different media types (movies, TV shows, music)
879
+
880
+ This configuration optimizes GitHub Copilot for ioBroker Emby adapter development, providing context-aware suggestions for WebSocket handling, media server integration, and ioBroker-specific patterns.