ember-mug 0.1.3 → 0.1.4
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/dist/cli.js +37 -7
- package/dist/components/App.d.ts +1 -1
- package/dist/components/App.js +64 -53
- package/dist/components/BatteryDisplay.d.ts +8 -3
- package/dist/components/BatteryDisplay.js +7 -18
- package/dist/components/ConnectionStatus.d.ts +7 -2
- package/dist/components/ConnectionStatus.js +5 -4
- package/dist/components/Header.d.ts +7 -2
- package/dist/components/Header.js +11 -3
- package/dist/components/HelpDisplay.d.ts +5 -2
- package/dist/components/HelpDisplay.js +8 -3
- package/dist/components/HorizontalRule.d.ts +2 -0
- package/dist/components/HorizontalRule.js +7 -0
- package/dist/components/Panel.d.ts +18 -0
- package/dist/components/Panel.js +57 -0
- package/dist/components/Presets.d.ts +8 -3
- package/dist/components/Presets.js +13 -8
- package/dist/components/SettingsView.d.ts +9 -3
- package/dist/components/SettingsView.js +82 -16
- package/dist/components/TemperatureControl.d.ts +8 -3
- package/dist/components/TemperatureControl.js +13 -18
- package/dist/components/TemperatureDisplay.d.ts +8 -3
- package/dist/components/TemperatureDisplay.js +8 -35
- package/dist/hooks/useMug.d.ts +1 -1
- package/dist/hooks/useMug.js +22 -22
- package/dist/lib/bluetooth.d.ts +2 -0
- package/dist/lib/bluetooth.js +8 -0
- package/dist/lib/mock-bluetooth.d.ts +65 -0
- package/dist/lib/mock-bluetooth.js +214 -0
- package/dist/lib/settings.d.ts +1 -1
- package/dist/lib/settings.js +20 -20
- package/dist/lib/theme.d.ts +135 -0
- package/dist/lib/theme.js +112 -0
- package/dist/lib/types.d.ts +0 -1
- package/dist/lib/types.js +12 -12
- package/dist/lib/utils.d.ts +1 -2
- package/dist/lib/utils.js +19 -35
- package/package.json +2 -1
- package/dist/components/ColorControl.d.ts +0 -9
- package/dist/components/ColorControl.js +0 -71
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { LiquidState, TemperatureUnit, MIN_TEMP_CELSIUS, MAX_TEMP_CELSIUS, BATTERY_DRAIN_RATE_HEATING, BATTERY_DRAIN_RATE_MAINTAINING, BATTERY_CHARGE_RATE, } from './types.js';
|
|
3
|
+
const DEFAULT_CONFIG = {
|
|
4
|
+
initialBattery: 75,
|
|
5
|
+
initialCurrentTemp: 45,
|
|
6
|
+
initialTargetTemp: 55,
|
|
7
|
+
initialLiquidState: LiquidState.Cooling,
|
|
8
|
+
initiallyCharging: false,
|
|
9
|
+
mugName: 'Mock Ember Mug',
|
|
10
|
+
scanDelay: 1500,
|
|
11
|
+
connectionDelay: 800,
|
|
12
|
+
tempChangeRate: 0.5, // degrees per second
|
|
13
|
+
updateInterval: 1000,
|
|
14
|
+
};
|
|
15
|
+
export class MockBluetoothManager extends EventEmitter {
|
|
16
|
+
config;
|
|
17
|
+
isConnected = false;
|
|
18
|
+
simulationInterval = null;
|
|
19
|
+
lastUpdateTime = Date.now();
|
|
20
|
+
state;
|
|
21
|
+
constructor(config = {}) {
|
|
22
|
+
super();
|
|
23
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
24
|
+
this.state = {
|
|
25
|
+
connected: false,
|
|
26
|
+
batteryLevel: this.config.initialBattery,
|
|
27
|
+
isCharging: this.config.initiallyCharging,
|
|
28
|
+
currentTemp: this.config.initialCurrentTemp,
|
|
29
|
+
targetTemp: this.config.initialTargetTemp,
|
|
30
|
+
liquidState: this.config.initialLiquidState,
|
|
31
|
+
temperatureUnit: TemperatureUnit.Celsius,
|
|
32
|
+
color: { r: 255, g: 147, b: 41, a: 255 }, // Default ember orange
|
|
33
|
+
mugName: '',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async startScanning() {
|
|
37
|
+
this.emit('scanning', true);
|
|
38
|
+
// Simulate finding a mug after a delay
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
this.emit('mugFound', this.config.mugName);
|
|
41
|
+
this.emit('scanning', false);
|
|
42
|
+
// Auto-connect after finding
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
this.connect();
|
|
45
|
+
}, this.config.connectionDelay);
|
|
46
|
+
}, this.config.scanDelay);
|
|
47
|
+
}
|
|
48
|
+
async stopScanning() {
|
|
49
|
+
this.emit('scanning', false);
|
|
50
|
+
}
|
|
51
|
+
async connect() {
|
|
52
|
+
this.isConnected = true;
|
|
53
|
+
this.state.connected = true;
|
|
54
|
+
this.state.mugName = this.config.mugName;
|
|
55
|
+
this.startSimulation();
|
|
56
|
+
this.emit('connected');
|
|
57
|
+
this.emitState();
|
|
58
|
+
}
|
|
59
|
+
startSimulation() {
|
|
60
|
+
this.lastUpdateTime = Date.now();
|
|
61
|
+
this.simulationInterval = setInterval(() => {
|
|
62
|
+
if (!this.isConnected)
|
|
63
|
+
return;
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const deltaSeconds = (now - this.lastUpdateTime) / 1000;
|
|
66
|
+
this.lastUpdateTime = now;
|
|
67
|
+
this.updateSimulation(deltaSeconds);
|
|
68
|
+
}, this.config.updateInterval);
|
|
69
|
+
}
|
|
70
|
+
updateSimulation(deltaSeconds) {
|
|
71
|
+
let stateChanged = false;
|
|
72
|
+
// Update temperature based on liquid state
|
|
73
|
+
if (this.state.liquidState !== LiquidState.Empty) {
|
|
74
|
+
const tempDiff = this.state.targetTemp - this.state.currentTemp;
|
|
75
|
+
const maxChange = this.config.tempChangeRate * deltaSeconds;
|
|
76
|
+
if (Math.abs(tempDiff) > 0.1) {
|
|
77
|
+
// Temperature is changing
|
|
78
|
+
const change = Math.sign(tempDiff) * Math.min(Math.abs(tempDiff), maxChange);
|
|
79
|
+
this.state.currentTemp = Math.round((this.state.currentTemp + change) * 100) / 100;
|
|
80
|
+
stateChanged = true;
|
|
81
|
+
// Update liquid state based on temperature direction
|
|
82
|
+
if (tempDiff > 0.5) {
|
|
83
|
+
if (this.state.liquidState !== LiquidState.Heating) {
|
|
84
|
+
this.state.liquidState = LiquidState.Heating;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (tempDiff < -0.5) {
|
|
88
|
+
if (this.state.liquidState !== LiquidState.Cooling) {
|
|
89
|
+
this.state.liquidState = LiquidState.Cooling;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Temperature is stable
|
|
95
|
+
if (this.state.liquidState !== LiquidState.StableTemperature) {
|
|
96
|
+
this.state.liquidState = LiquidState.StableTemperature;
|
|
97
|
+
stateChanged = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Update battery
|
|
102
|
+
if (this.state.isCharging) {
|
|
103
|
+
// Charging
|
|
104
|
+
const chargeGain = (BATTERY_CHARGE_RATE / 60) * deltaSeconds;
|
|
105
|
+
this.state.batteryLevel = Math.min(100, this.state.batteryLevel + chargeGain);
|
|
106
|
+
stateChanged = true;
|
|
107
|
+
}
|
|
108
|
+
else if (this.state.liquidState === LiquidState.Heating) {
|
|
109
|
+
// Draining while heating
|
|
110
|
+
const drain = (BATTERY_DRAIN_RATE_HEATING / 60) * deltaSeconds;
|
|
111
|
+
this.state.batteryLevel = Math.max(0, this.state.batteryLevel - drain);
|
|
112
|
+
stateChanged = true;
|
|
113
|
+
}
|
|
114
|
+
else if (this.state.liquidState === LiquidState.StableTemperature) {
|
|
115
|
+
// Draining while maintaining
|
|
116
|
+
const drain = (BATTERY_DRAIN_RATE_MAINTAINING / 60) * deltaSeconds;
|
|
117
|
+
this.state.batteryLevel = Math.max(0, this.state.batteryLevel - drain);
|
|
118
|
+
stateChanged = true;
|
|
119
|
+
}
|
|
120
|
+
if (stateChanged) {
|
|
121
|
+
this.emitState();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async setTargetTemp(temp) {
|
|
125
|
+
const clampedTemp = Math.max(MIN_TEMP_CELSIUS, Math.min(MAX_TEMP_CELSIUS, temp));
|
|
126
|
+
this.state.targetTemp = clampedTemp;
|
|
127
|
+
this.emitState();
|
|
128
|
+
}
|
|
129
|
+
async setTemperatureUnit(unit) {
|
|
130
|
+
this.state.temperatureUnit = unit;
|
|
131
|
+
this.emitState();
|
|
132
|
+
}
|
|
133
|
+
async setLedColor(color) {
|
|
134
|
+
this.state.color = { ...color };
|
|
135
|
+
this.emitState();
|
|
136
|
+
}
|
|
137
|
+
getState() {
|
|
138
|
+
return { ...this.state };
|
|
139
|
+
}
|
|
140
|
+
emitState() {
|
|
141
|
+
this.emit('stateChange', this.getState());
|
|
142
|
+
}
|
|
143
|
+
async disconnect() {
|
|
144
|
+
if (this.simulationInterval) {
|
|
145
|
+
clearInterval(this.simulationInterval);
|
|
146
|
+
this.simulationInterval = null;
|
|
147
|
+
}
|
|
148
|
+
if (this.isConnected) {
|
|
149
|
+
this.isConnected = false;
|
|
150
|
+
this.state.connected = false;
|
|
151
|
+
this.emit('disconnected');
|
|
152
|
+
this.emitState();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Mock-specific methods for testing scenarios
|
|
156
|
+
/** Simulate placing the mug on the charger */
|
|
157
|
+
simulateStartCharging() {
|
|
158
|
+
this.state.isCharging = true;
|
|
159
|
+
this.emitState();
|
|
160
|
+
}
|
|
161
|
+
/** Simulate removing the mug from the charger */
|
|
162
|
+
simulateStopCharging() {
|
|
163
|
+
this.state.isCharging = false;
|
|
164
|
+
this.emitState();
|
|
165
|
+
}
|
|
166
|
+
/** Simulate the mug becoming empty */
|
|
167
|
+
simulateEmpty() {
|
|
168
|
+
this.state.liquidState = LiquidState.Empty;
|
|
169
|
+
this.state.currentTemp = 0;
|
|
170
|
+
this.emitState();
|
|
171
|
+
}
|
|
172
|
+
/** Simulate filling the mug with liquid at a given temperature */
|
|
173
|
+
simulateFill(temperature) {
|
|
174
|
+
this.state.currentTemp = temperature;
|
|
175
|
+
this.state.liquidState = LiquidState.Filling;
|
|
176
|
+
this.emitState();
|
|
177
|
+
// Transition to appropriate state after a short delay
|
|
178
|
+
setTimeout(() => {
|
|
179
|
+
if (this.state.currentTemp < this.state.targetTemp) {
|
|
180
|
+
this.state.liquidState = LiquidState.Heating;
|
|
181
|
+
}
|
|
182
|
+
else if (this.state.currentTemp > this.state.targetTemp) {
|
|
183
|
+
this.state.liquidState = LiquidState.Cooling;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
this.state.liquidState = LiquidState.StableTemperature;
|
|
187
|
+
}
|
|
188
|
+
this.emitState();
|
|
189
|
+
}, 500);
|
|
190
|
+
}
|
|
191
|
+
/** Simulate a connection drop */
|
|
192
|
+
simulateDisconnect() {
|
|
193
|
+
this.disconnect();
|
|
194
|
+
}
|
|
195
|
+
/** Set battery level directly (for testing low battery scenarios) */
|
|
196
|
+
simulateBatteryLevel(level) {
|
|
197
|
+
this.state.batteryLevel = Math.max(0, Math.min(100, level));
|
|
198
|
+
this.emitState();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Singleton instance for mock mode
|
|
202
|
+
let mockInstance = null;
|
|
203
|
+
export function getMockBluetoothManager(config) {
|
|
204
|
+
if (!mockInstance) {
|
|
205
|
+
mockInstance = new MockBluetoothManager(config);
|
|
206
|
+
}
|
|
207
|
+
return mockInstance;
|
|
208
|
+
}
|
|
209
|
+
export function resetMockBluetoothManager() {
|
|
210
|
+
if (mockInstance) {
|
|
211
|
+
mockInstance.disconnect();
|
|
212
|
+
mockInstance = null;
|
|
213
|
+
}
|
|
214
|
+
}
|
package/dist/lib/settings.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppSettings, Preset, TemperatureUnit } from
|
|
1
|
+
import { AppSettings, Preset, TemperatureUnit } from "./types.js";
|
|
2
2
|
export declare function getSettings(): AppSettings;
|
|
3
3
|
export declare function getPresets(): Preset[];
|
|
4
4
|
export declare function setPresets(presets: Preset[]): void;
|
package/dist/lib/settings.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import Conf from
|
|
2
|
-
import { TemperatureUnit, DEFAULT_PRESETS, } from
|
|
1
|
+
import Conf from "conf";
|
|
2
|
+
import { TemperatureUnit, DEFAULT_PRESETS, } from "./types.js";
|
|
3
3
|
const config = new Conf({
|
|
4
|
-
projectName:
|
|
4
|
+
projectName: "ember-mug-cli",
|
|
5
5
|
defaults: {
|
|
6
6
|
presets: DEFAULT_PRESETS,
|
|
7
|
-
temperatureUnit: TemperatureUnit.
|
|
7
|
+
temperatureUnit: TemperatureUnit.Fahrenheit,
|
|
8
8
|
notifyOnTemperatureReached: true,
|
|
9
9
|
notifyAtBatteryPercentage: 15,
|
|
10
10
|
lastTargetTemp: 55,
|
|
@@ -13,17 +13,17 @@ const config = new Conf({
|
|
|
13
13
|
});
|
|
14
14
|
export function getSettings() {
|
|
15
15
|
return {
|
|
16
|
-
presets: config.get(
|
|
17
|
-
temperatureUnit: config.get(
|
|
18
|
-
notifyOnTemperatureReached: config.get(
|
|
19
|
-
notifyAtBatteryPercentage: config.get(
|
|
16
|
+
presets: config.get("presets"),
|
|
17
|
+
temperatureUnit: config.get("temperatureUnit"),
|
|
18
|
+
notifyOnTemperatureReached: config.get("notifyOnTemperatureReached"),
|
|
19
|
+
notifyAtBatteryPercentage: config.get("notifyAtBatteryPercentage"),
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
export function getPresets() {
|
|
23
|
-
return config.get(
|
|
23
|
+
return config.get("presets");
|
|
24
24
|
}
|
|
25
25
|
export function setPresets(presets) {
|
|
26
|
-
config.set(
|
|
26
|
+
config.set("presets", presets);
|
|
27
27
|
}
|
|
28
28
|
export function addPreset(preset) {
|
|
29
29
|
const presets = getPresets();
|
|
@@ -39,34 +39,34 @@ export function updatePreset(id, updates) {
|
|
|
39
39
|
setPresets(presets);
|
|
40
40
|
}
|
|
41
41
|
export function getTemperatureUnit() {
|
|
42
|
-
return config.get(
|
|
42
|
+
return config.get("temperatureUnit");
|
|
43
43
|
}
|
|
44
44
|
export function setTemperatureUnit(unit) {
|
|
45
|
-
config.set(
|
|
45
|
+
config.set("temperatureUnit", unit);
|
|
46
46
|
}
|
|
47
47
|
export function getLastTargetTemp() {
|
|
48
|
-
return config.get(
|
|
48
|
+
return config.get("lastTargetTemp");
|
|
49
49
|
}
|
|
50
50
|
export function setLastTargetTemp(temp) {
|
|
51
|
-
config.set(
|
|
51
|
+
config.set("lastTargetTemp", temp);
|
|
52
52
|
}
|
|
53
53
|
export function getLedColor() {
|
|
54
|
-
return config.get(
|
|
54
|
+
return config.get("ledColor");
|
|
55
55
|
}
|
|
56
56
|
export function setLedColor(color) {
|
|
57
|
-
config.set(
|
|
57
|
+
config.set("ledColor", color);
|
|
58
58
|
}
|
|
59
59
|
export function getNotifyOnTemperatureReached() {
|
|
60
|
-
return config.get(
|
|
60
|
+
return config.get("notifyOnTemperatureReached");
|
|
61
61
|
}
|
|
62
62
|
export function setNotifyOnTemperatureReached(enabled) {
|
|
63
|
-
config.set(
|
|
63
|
+
config.set("notifyOnTemperatureReached", enabled);
|
|
64
64
|
}
|
|
65
65
|
export function getNotifyAtBatteryPercentage() {
|
|
66
|
-
return config.get(
|
|
66
|
+
return config.get("notifyAtBatteryPercentage");
|
|
67
67
|
}
|
|
68
68
|
export function setNotifyAtBatteryPercentage(percentage) {
|
|
69
|
-
config.set(
|
|
69
|
+
config.set("notifyAtBatteryPercentage", percentage);
|
|
70
70
|
}
|
|
71
71
|
export function resetSettings() {
|
|
72
72
|
config.clear();
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { LiquidState } from './types.js';
|
|
2
|
+
export interface Theme {
|
|
3
|
+
primary: string;
|
|
4
|
+
secondary: string;
|
|
5
|
+
text: string;
|
|
6
|
+
dimText: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const THEMES: {
|
|
10
|
+
readonly heating: {
|
|
11
|
+
readonly primary: "#FF6B35";
|
|
12
|
+
readonly secondary: "#FF8C5A";
|
|
13
|
+
readonly text: "white";
|
|
14
|
+
readonly dimText: "#FFD4C4";
|
|
15
|
+
readonly name: "Heating";
|
|
16
|
+
};
|
|
17
|
+
readonly cooling: {
|
|
18
|
+
readonly primary: "#FF6B35";
|
|
19
|
+
readonly secondary: "#FF8C5A";
|
|
20
|
+
readonly text: "white";
|
|
21
|
+
readonly dimText: "#FFD4C4";
|
|
22
|
+
readonly name: "Cooling";
|
|
23
|
+
};
|
|
24
|
+
readonly stable: {
|
|
25
|
+
readonly primary: "#4A90D9";
|
|
26
|
+
readonly secondary: "#6BA3E0";
|
|
27
|
+
readonly text: "white";
|
|
28
|
+
readonly dimText: "#B8D4F0";
|
|
29
|
+
readonly name: "Perfect";
|
|
30
|
+
};
|
|
31
|
+
readonly empty: {
|
|
32
|
+
readonly primary: "#666666";
|
|
33
|
+
readonly secondary: "#888888";
|
|
34
|
+
readonly text: "white";
|
|
35
|
+
readonly dimText: "#AAAAAA";
|
|
36
|
+
readonly name: "Empty";
|
|
37
|
+
};
|
|
38
|
+
readonly filling: {
|
|
39
|
+
readonly primary: "#4A90D9";
|
|
40
|
+
readonly secondary: "#6BA3E0";
|
|
41
|
+
readonly text: "white";
|
|
42
|
+
readonly dimText: "#B8D4F0";
|
|
43
|
+
readonly name: "Filling";
|
|
44
|
+
};
|
|
45
|
+
readonly disconnected: {
|
|
46
|
+
readonly primary: "#444444";
|
|
47
|
+
readonly secondary: "#666666";
|
|
48
|
+
readonly text: "white";
|
|
49
|
+
readonly dimText: "#999999";
|
|
50
|
+
readonly name: "Disconnected";
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export declare const TERMINAL_COLORS: {
|
|
54
|
+
readonly heating: {
|
|
55
|
+
readonly primary: "yellow";
|
|
56
|
+
readonly secondary: "yellowBright";
|
|
57
|
+
readonly border: "yellow";
|
|
58
|
+
readonly text: "white";
|
|
59
|
+
readonly dimText: "gray";
|
|
60
|
+
};
|
|
61
|
+
readonly cooling: {
|
|
62
|
+
readonly primary: "yellow";
|
|
63
|
+
readonly secondary: "yellowBright";
|
|
64
|
+
readonly border: "yellow";
|
|
65
|
+
readonly text: "white";
|
|
66
|
+
readonly dimText: "gray";
|
|
67
|
+
};
|
|
68
|
+
readonly stable: {
|
|
69
|
+
readonly primary: "cyan";
|
|
70
|
+
readonly secondary: "cyanBright";
|
|
71
|
+
readonly border: "cyan";
|
|
72
|
+
readonly text: "white";
|
|
73
|
+
readonly dimText: "gray";
|
|
74
|
+
};
|
|
75
|
+
readonly empty: {
|
|
76
|
+
readonly primary: "gray";
|
|
77
|
+
readonly secondary: "white";
|
|
78
|
+
readonly border: "gray";
|
|
79
|
+
readonly text: "white";
|
|
80
|
+
readonly dimText: "gray";
|
|
81
|
+
};
|
|
82
|
+
readonly filling: {
|
|
83
|
+
readonly primary: "cyan";
|
|
84
|
+
readonly secondary: "cyanBright";
|
|
85
|
+
readonly border: "cyan";
|
|
86
|
+
readonly text: "white";
|
|
87
|
+
readonly dimText: "gray";
|
|
88
|
+
};
|
|
89
|
+
readonly disconnected: {
|
|
90
|
+
readonly primary: "gray";
|
|
91
|
+
readonly secondary: "white";
|
|
92
|
+
readonly border: "gray";
|
|
93
|
+
readonly text: "white";
|
|
94
|
+
readonly dimText: "gray";
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
export type ThemeKey = keyof typeof TERMINAL_COLORS;
|
|
98
|
+
export declare function getThemeForState(liquidState: LiquidState, connected: boolean): ThemeKey;
|
|
99
|
+
export declare function getTerminalTheme(themeKey: ThemeKey): {
|
|
100
|
+
readonly primary: "yellow";
|
|
101
|
+
readonly secondary: "yellowBright";
|
|
102
|
+
readonly border: "yellow";
|
|
103
|
+
readonly text: "white";
|
|
104
|
+
readonly dimText: "gray";
|
|
105
|
+
} | {
|
|
106
|
+
readonly primary: "yellow";
|
|
107
|
+
readonly secondary: "yellowBright";
|
|
108
|
+
readonly border: "yellow";
|
|
109
|
+
readonly text: "white";
|
|
110
|
+
readonly dimText: "gray";
|
|
111
|
+
} | {
|
|
112
|
+
readonly primary: "cyan";
|
|
113
|
+
readonly secondary: "cyanBright";
|
|
114
|
+
readonly border: "cyan";
|
|
115
|
+
readonly text: "white";
|
|
116
|
+
readonly dimText: "gray";
|
|
117
|
+
} | {
|
|
118
|
+
readonly primary: "gray";
|
|
119
|
+
readonly secondary: "white";
|
|
120
|
+
readonly border: "gray";
|
|
121
|
+
readonly text: "white";
|
|
122
|
+
readonly dimText: "gray";
|
|
123
|
+
} | {
|
|
124
|
+
readonly primary: "cyan";
|
|
125
|
+
readonly secondary: "cyanBright";
|
|
126
|
+
readonly border: "cyan";
|
|
127
|
+
readonly text: "white";
|
|
128
|
+
readonly dimText: "gray";
|
|
129
|
+
} | {
|
|
130
|
+
readonly primary: "gray";
|
|
131
|
+
readonly secondary: "white";
|
|
132
|
+
readonly border: "gray";
|
|
133
|
+
readonly text: "white";
|
|
134
|
+
readonly dimText: "gray";
|
|
135
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { LiquidState } from './types.js';
|
|
2
|
+
export const THEMES = {
|
|
3
|
+
heating: {
|
|
4
|
+
primary: '#FF6B35', // Orange
|
|
5
|
+
secondary: '#FF8C5A',
|
|
6
|
+
text: 'white',
|
|
7
|
+
dimText: '#FFD4C4',
|
|
8
|
+
name: 'Heating',
|
|
9
|
+
},
|
|
10
|
+
cooling: {
|
|
11
|
+
primary: '#FF6B35', // Orange (cooling down but not at temp yet)
|
|
12
|
+
secondary: '#FF8C5A',
|
|
13
|
+
text: 'white',
|
|
14
|
+
dimText: '#FFD4C4',
|
|
15
|
+
name: 'Cooling',
|
|
16
|
+
},
|
|
17
|
+
stable: {
|
|
18
|
+
primary: '#4A90D9', // Blue
|
|
19
|
+
secondary: '#6BA3E0',
|
|
20
|
+
text: 'white',
|
|
21
|
+
dimText: '#B8D4F0',
|
|
22
|
+
name: 'Perfect',
|
|
23
|
+
},
|
|
24
|
+
empty: {
|
|
25
|
+
primary: '#666666', // Grey
|
|
26
|
+
secondary: '#888888',
|
|
27
|
+
text: 'white',
|
|
28
|
+
dimText: '#AAAAAA',
|
|
29
|
+
name: 'Empty',
|
|
30
|
+
},
|
|
31
|
+
filling: {
|
|
32
|
+
primary: '#4A90D9', // Blue
|
|
33
|
+
secondary: '#6BA3E0',
|
|
34
|
+
text: 'white',
|
|
35
|
+
dimText: '#B8D4F0',
|
|
36
|
+
name: 'Filling',
|
|
37
|
+
},
|
|
38
|
+
disconnected: {
|
|
39
|
+
primary: '#444444',
|
|
40
|
+
secondary: '#666666',
|
|
41
|
+
text: 'white',
|
|
42
|
+
dimText: '#999999',
|
|
43
|
+
name: 'Disconnected',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
// Terminal color mappings (closest ANSI colors)
|
|
47
|
+
export const TERMINAL_COLORS = {
|
|
48
|
+
heating: {
|
|
49
|
+
primary: 'yellow', // Closest to orange in terminal
|
|
50
|
+
secondary: 'yellowBright',
|
|
51
|
+
border: 'yellow',
|
|
52
|
+
text: 'white',
|
|
53
|
+
dimText: 'gray',
|
|
54
|
+
},
|
|
55
|
+
cooling: {
|
|
56
|
+
primary: 'yellow',
|
|
57
|
+
secondary: 'yellowBright',
|
|
58
|
+
border: 'yellow',
|
|
59
|
+
text: 'white',
|
|
60
|
+
dimText: 'gray',
|
|
61
|
+
},
|
|
62
|
+
stable: {
|
|
63
|
+
primary: 'cyan',
|
|
64
|
+
secondary: 'cyanBright',
|
|
65
|
+
border: 'cyan',
|
|
66
|
+
text: 'white',
|
|
67
|
+
dimText: 'gray',
|
|
68
|
+
},
|
|
69
|
+
empty: {
|
|
70
|
+
primary: 'gray',
|
|
71
|
+
secondary: 'white',
|
|
72
|
+
border: 'gray',
|
|
73
|
+
text: 'white',
|
|
74
|
+
dimText: 'gray',
|
|
75
|
+
},
|
|
76
|
+
filling: {
|
|
77
|
+
primary: 'cyan',
|
|
78
|
+
secondary: 'cyanBright',
|
|
79
|
+
border: 'cyan',
|
|
80
|
+
text: 'white',
|
|
81
|
+
dimText: 'gray',
|
|
82
|
+
},
|
|
83
|
+
disconnected: {
|
|
84
|
+
primary: 'gray',
|
|
85
|
+
secondary: 'white',
|
|
86
|
+
border: 'gray',
|
|
87
|
+
text: 'white',
|
|
88
|
+
dimText: 'gray',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
export function getThemeForState(liquidState, connected) {
|
|
92
|
+
if (!connected) {
|
|
93
|
+
return 'disconnected';
|
|
94
|
+
}
|
|
95
|
+
switch (liquidState) {
|
|
96
|
+
case LiquidState.Empty:
|
|
97
|
+
return 'empty';
|
|
98
|
+
case LiquidState.Filling:
|
|
99
|
+
return 'filling';
|
|
100
|
+
case LiquidState.Heating:
|
|
101
|
+
return 'heating';
|
|
102
|
+
case LiquidState.Cooling:
|
|
103
|
+
return 'cooling';
|
|
104
|
+
case LiquidState.StableTemperature:
|
|
105
|
+
return 'stable';
|
|
106
|
+
default:
|
|
107
|
+
return 'empty';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function getTerminalTheme(themeKey) {
|
|
111
|
+
return TERMINAL_COLORS[themeKey];
|
|
112
|
+
}
|
package/dist/lib/types.d.ts
CHANGED
package/dist/lib/types.js
CHANGED
|
@@ -12,20 +12,20 @@ export var TemperatureUnit;
|
|
|
12
12
|
TemperatureUnit[TemperatureUnit["Fahrenheit"] = 1] = "Fahrenheit";
|
|
13
13
|
})(TemperatureUnit || (TemperatureUnit = {}));
|
|
14
14
|
export const DEFAULT_PRESETS = [
|
|
15
|
-
{ id:
|
|
16
|
-
{ id:
|
|
17
|
-
{ id:
|
|
15
|
+
{ id: "1", name: "Latte", temperature: 52.0 },
|
|
16
|
+
{ id: "2", name: "Coffee", temperature: 55.56 },
|
|
17
|
+
{ id: "3", name: "Tea", temperature: 60.0 },
|
|
18
18
|
];
|
|
19
|
-
export const EMBER_SERVICE_UUID =
|
|
19
|
+
export const EMBER_SERVICE_UUID = "fc543622236c4c948fa9944a3e5353fa";
|
|
20
20
|
export const EMBER_CHARACTERISTICS = {
|
|
21
|
-
MUG_NAME:
|
|
22
|
-
CURRENT_TEMP:
|
|
23
|
-
TARGET_TEMP:
|
|
24
|
-
TEMP_UNIT:
|
|
25
|
-
BATTERY:
|
|
26
|
-
LIQUID_STATE:
|
|
27
|
-
PUSH_EVENTS:
|
|
28
|
-
LED_COLOR:
|
|
21
|
+
MUG_NAME: "fc540001236c4c948fa9944a3e5353fa",
|
|
22
|
+
CURRENT_TEMP: "fc540002236c4c948fa9944a3e5353fa",
|
|
23
|
+
TARGET_TEMP: "fc540003236c4c948fa9944a3e5353fa",
|
|
24
|
+
TEMP_UNIT: "fc540004236c4c948fa9944a3e5353fa",
|
|
25
|
+
BATTERY: "fc540007236c4c948fa9944a3e5353fa",
|
|
26
|
+
LIQUID_STATE: "fc540008236c4c948fa9944a3e5353fa",
|
|
27
|
+
PUSH_EVENTS: "fc540012236c4c948fa9944a3e5353fa",
|
|
28
|
+
LED_COLOR: "fc540014236c4c948fa9944a3e5353fa",
|
|
29
29
|
};
|
|
30
30
|
export const MIN_TEMP_CELSIUS = 50;
|
|
31
31
|
export const MAX_TEMP_CELSIUS = 63;
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { TemperatureUnit, LiquidState } from
|
|
1
|
+
import { TemperatureUnit, LiquidState } from "./types.js";
|
|
2
2
|
export declare function formatTemperature(temp: number, unit: TemperatureUnit): string;
|
|
3
3
|
export declare function celsiusToFahrenheit(celsius: number): number;
|
|
4
4
|
export declare function fahrenheitToCelsius(fahrenheit: number): number;
|
|
5
5
|
export declare function formatBatteryLevel(level: number): string;
|
|
6
6
|
export declare function getBatteryIcon(level: number, isCharging: boolean): string;
|
|
7
7
|
export declare function getLiquidStateText(state: LiquidState): string;
|
|
8
|
-
export declare function getLiquidStateIcon(state: LiquidState): string;
|
|
9
8
|
export declare function clampTemperature(temp: number): number;
|
|
10
9
|
export declare function formatDuration(minutes: number): string;
|
|
11
10
|
export declare function estimateTimeToTargetTemp(currentTemp: number, targetTemp: number, liquidState: LiquidState): number | null;
|