iobroker.faikout 0.0.5

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.
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "iobroker.faikout",
3
+ "version": "0.0.5",
4
+ "description": "Connect Daikin air conditioners with a faikout module locally via MQTT.",
5
+ "author": "Immanuel",
6
+ "homepage": "https://github.com/SmarthomeElektroniker/ioBroker.faikout",
7
+ "license": "MIT",
8
+ "keywords": [
9
+ "ioBroker",
10
+ "faikout",
11
+ "faikin",
12
+ "daikin",
13
+ "mqtt",
14
+ "climate"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/SmarthomeElektroniker/ioBroker.faikout.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/SmarthomeElektroniker/ioBroker.faikout/issues"
22
+ },
23
+ "engines": {
24
+ "node": ">=22"
25
+ },
26
+ "dependencies": {
27
+ "@iobroker/adapter-core": "^3.4.1",
28
+ "aedes": "^1.1.1"
29
+ },
30
+ "devDependencies": {
31
+ "@eslint/js": "^9.39.0",
32
+ "@iobroker/adapter-dev": "^1.5.0",
33
+ "@iobroker/testing": "^5.2.2",
34
+ "@types/node": "^22.18.11",
35
+ "eslint": "^9.39.0",
36
+ "globals": "^15.15.0",
37
+ "mqtt": "^5.15.2",
38
+ "proxyquire": "^2.1.3"
39
+ },
40
+ "main": "main.js",
41
+ "files": [
42
+ "LICENSE",
43
+ "admin/",
44
+ "io-package.json",
45
+ "lib/",
46
+ "main.js",
47
+ "widgets/"
48
+ ],
49
+ "scripts": {
50
+ "test:package": "mocha test/package --exit",
51
+ "test:integration": "mocha test/integration --exit",
52
+ "test": "npm run test:package && npm run test:unit",
53
+ "lint": "eslint .",
54
+ "test:unit": "mocha test/verbrauch test/broker --exit"
55
+ }
56
+ }
@@ -0,0 +1,233 @@
1
+ /*
2
+ * Bausteine der Klima-Kachel fuer VIS 1.x
3
+ *
4
+ * Jedes Widget bringt seinen eigenen Rahmen mit und ist einzeln platzierbar. Alle Farben
5
+ * kommen aus CSS-Variablen; das Attribut "theme" schaltet zwischen hell und dunkel um.
6
+ * Der Akzent folgt der Betriebsart, der Bogen ist eine Temperaturskala von kalt nach heiss.
7
+ */
8
+
9
+ .fk {
10
+ --fk-flaeche: #ffffff;
11
+ --fk-flaeche-2: #f4f6f9;
12
+ --fk-rand: #d6dce5;
13
+ --fk-rand-stark: #b9c3d0;
14
+ --fk-text: #161d28;
15
+ --fk-leise: #5b6878;
16
+ --fk-still: #8593a4;
17
+ --fk-akzent: #1e86c4;
18
+ --fk-gut: #2e7d52;
19
+ --fk-warn: #b4761b;
20
+
21
+ /* Temperaturskala 16 bis 29 Grad */
22
+ --fk-t0: #2e6fbf; --fk-t1: #4fa9e0; --fk-t2: #7fbf8e;
23
+ --fk-t3: #d9be55; --fk-t4: #de8438; --fk-t5: #c4402a;
24
+
25
+ box-sizing: border-box;
26
+ width: 100%;
27
+ height: 100%;
28
+ overflow: hidden;
29
+ background: var(--fk-flaeche);
30
+ color: var(--fk-text);
31
+ border: 1px solid var(--fk-rand);
32
+ border-radius: 14px;
33
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
34
+ line-height: 1.5;
35
+ display: flex;
36
+ flex-direction: column;
37
+ }
38
+ .fk * { box-sizing: border-box; }
39
+
40
+ .fk.fk-dunkel {
41
+ --fk-flaeche: #171e27;
42
+ --fk-flaeche-2: #1e2733;
43
+ --fk-rand: #2a3542;
44
+ --fk-rand-stark: #3b4959;
45
+ --fk-text: #e8edf3;
46
+ --fk-leise: #9faebf;
47
+ --fk-still: #6f8093;
48
+ --fk-gut: #56c08a;
49
+ --fk-warn: #e0a84a;
50
+ --fk-t0: #4a8fe0; --fk-t1: #63beee; --fk-t2: #8fd09c;
51
+ --fk-t3: #e7d06a; --fk-t4: #f0954b; --fk-t5: #e2543a;
52
+ }
53
+ /* Ohne Rahmen - wenn das Widget in eine vorhandene Flaeche eingesetzt wird */
54
+ .fk.fk-blank { border: none; border-radius: 0; background: transparent; }
55
+
56
+ .fk-fehler { padding: 14px; color: var(--fk-warn); font-size: 13px; }
57
+
58
+ /* ---------------------------------------------------------------- Kopf */
59
+ .fk-kopf { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 13px 16px 0; flex: none; }
60
+ .fk-name { font-weight: 600; font-size: 15px; letter-spacing: -.01em; }
61
+ .fk-ort { font-size: 11px; color: var(--fk-still); letter-spacing: .05em; }
62
+
63
+ .fk-schalter {
64
+ position: relative; width: 48px; height: 27px; border-radius: 999px; flex: none;
65
+ border: 1px solid var(--fk-rand-stark); background: var(--fk-flaeche-2);
66
+ cursor: pointer; padding: 0; transition: background .2s, border-color .2s;
67
+ }
68
+ .fk-schalter::after {
69
+ content: ""; position: absolute; top: 2px; left: 2px; width: 21px; height: 21px;
70
+ border-radius: 50%; background: var(--fk-still);
71
+ transition: transform .22s cubic-bezier(.4,1.2,.5,1), background .22s;
72
+ }
73
+ .fk-schalter.fk-an { border-color: var(--fk-akzent); }
74
+ .fk-schalter.fk-an::after { transform: translateX(21px); background: var(--fk-akzent); }
75
+ .fk-schalter:focus-visible { outline: 2px solid var(--fk-akzent); outline-offset: 2px; }
76
+
77
+ .fk-streifen { height: 3px; flex: none; background: var(--fk-akzent); transition: background .35s ease; }
78
+ .fk.fk-aus .fk-streifen { background: var(--fk-rand-stark); }
79
+
80
+ /* ---------------------------------------------------------------- Bogen */
81
+ .fk-instrument { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 6px 12px 0; flex: 1 1 auto; min-height: 0; }
82
+ .fk-bogen { position: relative; width: 100%; max-width: 320px; aspect-ratio: 320 / 258; touch-action: none; }
83
+ .fk-bogen svg { display: block; width: 100%; height: 100%; overflow: visible; }
84
+ /* KEIN stroke in CSS: das wuerde das per-Widget gesetzte Verlaufsattribut schlagen
85
+ (CSS gewinnt gegen Praesentationsattribute) und der Bogen bliebe farblos. */
86
+ .fk-spur { fill: none; stroke-width: 17; stroke-linecap: round; }
87
+ .fk-skala { fill: none; stroke-width: 17; stroke-linecap: round; opacity: .2; }
88
+ .fk-fuellung { fill: none; stroke-width: 17; stroke-linecap: round; transition: stroke-dashoffset .35s cubic-bezier(.3,.8,.4,1), opacity .3s; }
89
+ .fk-bogen.fk-zieht .fk-fuellung { transition: none; }
90
+ .fk.fk-aus .fk-fuellung { opacity: .3; }
91
+ .fk.fk-aus .fk-skala { opacity: .1; }
92
+
93
+ .fk-treffer { fill: none; stroke: transparent; stroke-width: 46; cursor: pointer; }
94
+ .fk-griff { pointer-events: none; transition: transform .35s cubic-bezier(.3,.8,.4,1); }
95
+ .fk-bogen.fk-zieht .fk-griff { transition: none; }
96
+ .fk-griff circle { fill: var(--fk-flaeche); stroke: var(--fk-akzent); stroke-width: 4.5; }
97
+ .fk-griff .fk-kern { fill: var(--fk-akzent); stroke: none; }
98
+ .fk-raummarke { fill: none; stroke: var(--fk-text); stroke-width: 2.5; stroke-linecap: round; opacity: .6; }
99
+
100
+ .fk-ablesung { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; padding-top: 4%; pointer-events: none; }
101
+ .fk-soll { display: flex; align-items: flex-start; justify-content: center; gap: 3px; }
102
+ .fk-soll-zahl { font-size: 56px; font-weight: 300; letter-spacing: -.04em; line-height: .92; font-variant-numeric: tabular-nums; }
103
+ .fk-soll-einheit { font-size: 25px; font-weight: 500; color: var(--fk-leise); margin-top: 6px; }
104
+ .fk-soll-etikett { font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--fk-still); margin-top: 5px; }
105
+ .fk-ist { display: flex; align-items: center; gap: 18px; margin-top: 16px; font-size: 18px; color: var(--fk-leise); font-variant-numeric: tabular-nums; }
106
+ .fk-ist span { display: inline-flex; align-items: baseline; gap: 5px; }
107
+ .fk-ist b { font-weight: 600; color: var(--fk-text); font-size: 21px; }
108
+ .fk-ist svg { width: 17px; height: 17px; opacity: .6; align-self: center; }
109
+
110
+ .fk-pm-reihe { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: -34px; padding-bottom: 10px; position: relative; z-index: 1; flex: none; }
111
+ .fk-pm {
112
+ width: 76px; height: 40px; border-radius: 12px; border: 1px solid var(--fk-rand-stark);
113
+ background: var(--fk-flaeche-2); color: var(--fk-text); font-size: 20px; cursor: pointer;
114
+ display: grid; place-items: center; padding: 0;
115
+ transition: border-color .15s, background .15s, color .15s, transform .1s;
116
+ }
117
+ /* Waermer/kaelter statt Akzentfarbe: die Taste zeigt die Richtung, nicht die Betriebsart.
118
+ Sonst leuchtete im Heizbetrieb auch die Minus-Taste bernsteinfarben. */
119
+ .fk-pm-plus:hover { border-color: var(--fk-t5); color: var(--fk-t5); background: color-mix(in srgb, var(--fk-t5) 12%, var(--fk-flaeche-2)); }
120
+ .fk-pm-minus:hover { border-color: var(--fk-t0); color: var(--fk-t0); background: color-mix(in srgb, var(--fk-t0) 12%, var(--fk-flaeche-2)); }
121
+ .fk-pm:active { transform: scale(.94); }
122
+ .fk-pm-plus:focus-visible { outline: 2px solid var(--fk-t5); outline-offset: 2px; }
123
+ .fk-pm-minus:focus-visible { outline: 2px solid var(--fk-t0); outline-offset: 2px; }
124
+
125
+ /* ---------------------------------------------------------------- Auswahlreihen */
126
+ .fk-gruppe { padding: 10px 14px; flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
127
+ .fk-gruppe-etikett { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--fk-still); margin-bottom: 8px; flex: none; }
128
+ .fk-pillen { display: flex; flex-wrap: wrap; gap: 6px; align-content: flex-start; overflow: auto; }
129
+ .fk-pillen.fk-senkrecht { flex-direction: column; flex-wrap: nowrap; }
130
+ .fk-pillen.fk-senkrecht .fk-pille { width: 100%; justify-content: flex-start; }
131
+
132
+ .fk-pille {
133
+ display: inline-flex; align-items: center; gap: 7px;
134
+ border: 1px solid var(--fk-rand); background: var(--fk-flaeche-2); color: var(--fk-leise);
135
+ border-radius: 999px; padding: 6px 13px 6px 10px; font-size: 12.5px; cursor: pointer;
136
+ font-family: inherit; transition: border-color .15s, background .15s, color .15s;
137
+ }
138
+ .fk-pille svg { width: 15px; height: 15px; flex: none; }
139
+ .fk-pille:hover { border-color: var(--fk-rand-stark); color: var(--fk-text); }
140
+ .fk-pille.fk-gewaehlt { border-color: var(--fk-akzent); color: var(--fk-akzent); font-weight: 600; background: color-mix(in srgb, var(--fk-akzent) 12%, transparent); }
141
+ .fk-pille:focus-visible { outline: 2px solid var(--fk-akzent); outline-offset: 2px; }
142
+ .fk-pille.fk-nur-text { padding: 6px 14px; min-width: 42px; justify-content: center; }
143
+ .fk-pillen.fk-senkrecht .fk-pille.fk-nur-text { justify-content: flex-start; }
144
+
145
+ /* Zusatzfunktionen als reine Symbolschalter - der Name steht im Tooltip. Groesseres
146
+ Symbol und rundes Feld, damit sie sich klar von den Auswahlreihen unterscheiden. */
147
+ .fk-pille.fk-nur-icon { padding: 0; width: 42px; height: 42px; border-radius: 50%; justify-content: center; gap: 0; }
148
+ .fk-pille.fk-nur-icon svg { width: 20px; height: 20px; }
149
+
150
+ /* ---------------------------------------------------------------- Leistungsbegrenzung */
151
+ .fk-leistung { display: flex; flex-direction: column; gap: 8px; }
152
+ .fk-leistung-wert { font-size: 32px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.02em; line-height: 1.05; text-align: center; }
153
+ .fk-leistung-wert small { font-size: 15px; font-weight: 500; color: var(--fk-leise); margin-left: 2px; }
154
+ .fk-leistung-skala { display: flex; justify-content: space-between; font-size: 10px; color: var(--fk-still); font-variant-numeric: tabular-nums; }
155
+
156
+ .fk-schieber { -webkit-appearance: none; appearance: none; width: 100%; height: 8px; border-radius: 999px; background: var(--fk-rand); outline: none; margin: 0; }
157
+ .fk-schieber::-webkit-slider-thumb {
158
+ -webkit-appearance: none; appearance: none; width: 24px; height: 24px; border-radius: 50%;
159
+ background: var(--fk-flaeche); border: 4px solid var(--fk-akzent); cursor: pointer;
160
+ box-shadow: 0 1px 3px rgba(0,0,0,.25);
161
+ }
162
+ .fk-schieber::-moz-range-thumb {
163
+ width: 20px; height: 20px; border-radius: 50%;
164
+ background: var(--fk-flaeche); border: 4px solid var(--fk-akzent); cursor: pointer;
165
+ }
166
+ .fk-schieber:focus-visible { outline: 2px solid var(--fk-akzent); outline-offset: 4px; }
167
+
168
+ /* ---------------------------------------------------------------- Messwerte */
169
+ .fk-messwerte { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1px; background: var(--fk-rand); flex: 1 1 auto; align-content: start; overflow: auto; }
170
+ .fk-mw { background: var(--fk-flaeche); padding: 10px 12px; text-align: center; }
171
+ .fk-mw-name { font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--fk-still); white-space: nowrap; }
172
+ .fk-mw-wert { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; margin-top: 2px; }
173
+ .fk-mw-wert small { font-size: 11px; font-weight: 500; color: var(--fk-leise); margin-left: 1px; }
174
+
175
+ /* Einzelwert als eigenes Element - groesser, weil er alleine steht */
176
+ .fk-einzel { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; padding: 10px 12px; text-align: center; }
177
+ .fk-einzel-name { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--fk-still); }
178
+ .fk-einzel-wert { font-size: 30px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.02em; line-height: 1.1; margin-top: 3px; }
179
+ .fk-einzel-wert small { font-size: 14px; font-weight: 500; color: var(--fk-leise); margin-left: 2px; }
180
+
181
+ /* ---------------------------------------------------------------- Fuss */
182
+ .fk-fuss { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 16px; border-top: 1px solid var(--fk-rand); background: var(--fk-flaeche-2); font-size: 10.5px; color: var(--fk-still); flex: none; }
183
+ .fk-punkt { display: inline-flex; align-items: center; gap: 6px; }
184
+ .fk-punkt::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--fk-gut); }
185
+ .fk-punkt.fk-weg::before { background: var(--fk-still); }
186
+ .fk-status.fk-wartet { color: var(--fk-warn); }
187
+
188
+ @media (prefers-reduced-motion: reduce) { .fk * { transition: none !important; } }
189
+
190
+ /* ---------------------------------------------------------------- Energieverlauf */
191
+
192
+ .fk-verlauf { padding: 0 12px 10px; }
193
+
194
+ .fk-v-schalter {
195
+ display: inline-flex; gap: 3px; padding: 3px;
196
+ background: rgba(0, 0, 0, .06); border-radius: 7px; margin-bottom: 6px;
197
+ }
198
+ .fk-dunkel .fk-v-schalter { background: rgba(255, 255, 255, .06); }
199
+ .fk-v-schalter button {
200
+ border: 0; background: transparent; color: inherit; opacity: .6;
201
+ font: inherit; font-size: 12px; padding: 4px 11px;
202
+ border-radius: 5px; cursor: pointer;
203
+ }
204
+ .fk-v-schalter button:hover { opacity: 1; }
205
+ .fk-v-schalter button[aria-pressed="true"] {
206
+ background: rgba(0, 0, 0, .12); opacity: 1; font-weight: 600;
207
+ }
208
+ .fk-dunkel .fk-v-schalter button[aria-pressed="true"] { background: rgba(255, 255, 255, .14); }
209
+
210
+ .fk-v-arten { display: flex; gap: 13px; margin: 2px 0 6px; }
211
+ .fk-v-arten label {
212
+ display: flex; align-items: center; gap: 5px;
213
+ font-size: 12px; opacity: .65; cursor: pointer;
214
+ }
215
+ .fk-v-arten label:hover { opacity: 1; }
216
+ .fk-v-arten input { margin: 0; cursor: pointer; accent-color: var(--fk-akzent, #1e86c4); }
217
+ .fk-v-punkt { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
218
+
219
+ .fk-v-flaeche { position: relative; }
220
+ .fk-verlauf-svg { display: block; width: 100%; height: auto; }
221
+ /* Die Achsen sollen sich unterordnen - die Saeulen tragen die Aussage. */
222
+ .fk-v-achse text { fill: currentColor; opacity: .5; font-size: 9px; }
223
+ .fk-v-raster line { stroke: currentColor; opacity: .12; stroke-width: 1; }
224
+ .fk-v-balken rect { transition: opacity .1s; }
225
+ .fk-v-balken rect:hover { opacity: .75; }
226
+
227
+ .fk-v-leer { padding: 22px 0; text-align: center; font-size: 12px; opacity: .5; }
228
+
229
+ .fk-v-fuss {
230
+ display: flex; justify-content: space-between; align-items: baseline;
231
+ margin-top: 6px; font-size: 12px; opacity: .65;
232
+ }
233
+ .fk-v-fuss b { font-weight: 600; opacity: 1; font-size: 13px; }