@glasshome/sync-layer 0.3.1 → 0.4.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.
|
@@ -53,18 +53,18 @@ export interface OAuthOptions {
|
|
|
53
53
|
* Authenticate using OAuth flow
|
|
54
54
|
*
|
|
55
55
|
* This function uses `getAuth` from `home-assistant-js-websocket` which:
|
|
56
|
-
* 1.
|
|
56
|
+
* 1. Redirects the whole page to the Home Assistant authorization page
|
|
57
57
|
* 2. Waits for user authorization
|
|
58
|
-
* 3.
|
|
59
|
-
* 4.
|
|
58
|
+
* 3. On return, HA redirects back to the app URL with `?auth_callback=1&code=…`
|
|
59
|
+
* 4. Exchanges the authorization code for tokens and returns an Auth object
|
|
60
60
|
*
|
|
61
61
|
* Note: For a complete OAuth flow, you typically need to:
|
|
62
62
|
* - Set up a redirect URI endpoint in your app
|
|
63
63
|
* - Handle the OAuth callback with the authorization code
|
|
64
64
|
* - Use `getAuthCode` callback to provide the code
|
|
65
65
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
66
|
+
* In browser environments `getAuth` handles this automatically via a full-page
|
|
67
|
+
* redirect (not a popup); native platforms supply `getAuthCode` instead.
|
|
68
68
|
*
|
|
69
69
|
* @param options - OAuth options
|
|
70
70
|
* @returns Authenticated Auth object
|
|
@@ -89,6 +89,12 @@ export interface DemoStatisticBucket {
|
|
|
89
89
|
max: number;
|
|
90
90
|
/** Energy in Wh accumulated over the bucket (W-h integration). */
|
|
91
91
|
sum: number;
|
|
92
|
+
/**
|
|
93
|
+
* Energy added during this bucket, in kWh, matching HA's `change` field for
|
|
94
|
+
* an energy (kWh) sensor. This is the per-bucket delta (not cumulative), the
|
|
95
|
+
* value daily-total consumers read.
|
|
96
|
+
*/
|
|
97
|
+
change: number;
|
|
92
98
|
}
|
|
93
99
|
/**
|
|
94
100
|
* Synthesize statistics buckets for one energy entity across a window.
|
|
@@ -160,6 +160,23 @@ function evChargerPower(lt) {
|
|
|
160
160
|
const ramp = into < 2 ? into / 2 : 1;
|
|
161
161
|
return 7200 * ramp;
|
|
162
162
|
}
|
|
163
|
+
const HEAT_PUMP_PEAK_W = 3200;
|
|
164
|
+
/**
|
|
165
|
+
* A smooth heat-pump load: a slow morning/evening envelope carrying a
|
|
166
|
+
* few-minute shimmer. Its peaks push the home deficit past the battery's power
|
|
167
|
+
* cap, so grid import (and the running cost) visibly rise and fall on live
|
|
168
|
+
* ticks instead of sitting at zero. Gated out of the deep-night window so the
|
|
169
|
+
* quiet 02:00-03:00 baseline is unchanged.
|
|
170
|
+
*/
|
|
171
|
+
function heatPumpPower(lt) {
|
|
172
|
+
const h = lt.hourFrac;
|
|
173
|
+
let envelope = 0;
|
|
174
|
+
if (h >= 6 && h <= 9) envelope = Math.sin((h - 6) / 3 * Math.PI);
|
|
175
|
+
else if (h >= 16 && h <= 23) envelope = Math.sin((h - 16) / 7 * Math.PI);
|
|
176
|
+
else return 0;
|
|
177
|
+
const shimmer = .7 + .3 * Math.sin(lt.minuteOfDay * Math.PI * 2);
|
|
178
|
+
return HEAT_PUMP_PEAK_W * envelope * shimmer;
|
|
179
|
+
}
|
|
163
180
|
/** Smooth occupancy envelope: extra load mornings and evenings. */
|
|
164
181
|
function occupancyEnvelope(lt) {
|
|
165
182
|
const h = lt.hourFrac;
|
|
@@ -228,7 +245,7 @@ function batterySoc(lt, midnightMs) {
|
|
|
228
245
|
return a + (b - a) * frac;
|
|
229
246
|
}
|
|
230
247
|
function homePower(lt) {
|
|
231
|
-
const base = ALWAYS_ON_W + fridgePower(lt) + kettlePower(lt) + ovenPower(lt) + dishwasherPower(lt) + washerPower(lt) + evChargerPower(lt) + occupancyEnvelope(lt);
|
|
248
|
+
const base = ALWAYS_ON_W + fridgePower(lt) + kettlePower(lt) + ovenPower(lt) + dishwasherPower(lt) + washerPower(lt) + evChargerPower(lt) + heatPumpPower(lt) + occupancyEnvelope(lt);
|
|
232
249
|
return base * homeNoise(lt);
|
|
233
250
|
}
|
|
234
251
|
/**
|
|
@@ -416,7 +433,8 @@ function synthesizeEnergyStatistics(entityId, startMs, endMs, period) {
|
|
|
416
433
|
mean,
|
|
417
434
|
min: min === Number.POSITIVE_INFINITY ? mean : min,
|
|
418
435
|
max: max === Number.NEGATIVE_INFINITY ? mean : max,
|
|
419
|
-
sum: cumulativeWh
|
|
436
|
+
sum: cumulativeWh,
|
|
437
|
+
change: energyWh / 1e3
|
|
420
438
|
});
|
|
421
439
|
}
|
|
422
440
|
return buckets;
|
|
@@ -851,6 +869,26 @@ function createDemoFixtures() {
|
|
|
851
869
|
area_id: "entry",
|
|
852
870
|
supported_features: 1
|
|
853
871
|
});
|
|
872
|
+
add(makeEntity("light.studio_rgb", "on", {
|
|
873
|
+
brightness: 205,
|
|
874
|
+
hs_color: [265, 66],
|
|
875
|
+
color_mode: "hs",
|
|
876
|
+
supported_color_modes: ["hs", "color_temp"]
|
|
877
|
+
}), {
|
|
878
|
+
device_id: "studio_lights",
|
|
879
|
+
area_id: "living_room",
|
|
880
|
+
supported_features: 44
|
|
881
|
+
});
|
|
882
|
+
add(makeEntity("light.desk_rgb", "on", {
|
|
883
|
+
brightness: 200,
|
|
884
|
+
hs_color: [172, 79],
|
|
885
|
+
color_mode: "hs",
|
|
886
|
+
supported_color_modes: ["hs", "color_temp"]
|
|
887
|
+
}), {
|
|
888
|
+
device_id: "desk_lights",
|
|
889
|
+
area_id: "bedroom",
|
|
890
|
+
supported_features: 44
|
|
891
|
+
});
|
|
854
892
|
add(makeEntity("sensor.temperature_living", "22.5", {
|
|
855
893
|
unit_of_measurement: "°C",
|
|
856
894
|
device_class: "temperature",
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ENERGY_ENTITY_IDS, createDemoFixtures, demoEnergyPreferences, energyEntityValue, formatEnergyState, isEnergyEntity, isSunUp, resetStore, setState, simulateEnergy, state, sunEvents, synthesizeEnergyHistory, synthesizeEnergyStatistics } from "./demo-data-
|
|
1
|
+
import { ENERGY_ENTITY_IDS, createDemoFixtures, demoEnergyPreferences, energyEntityValue, formatEnergyState, isEnergyEntity, isSunUp, resetStore, setState, simulateEnergy, state, sunEvents, synthesizeEnergyHistory, synthesizeEnergyStatistics } from "./demo-data-CcG2efdQ.js";
|
|
2
2
|
import { runHaBridgeWorker } from "./worker-main-DTbzEKor.js";
|
|
3
3
|
import { createConnection, createLongLivedTokenAuth, getAuth } from "home-assistant-js-websocket";
|
|
4
4
|
import { produce, reconcile } from "solid-js/store";
|
|
@@ -247,18 +247,18 @@ function updateStatisticsMetadata(metadata) {
|
|
|
247
247
|
* Authenticate using OAuth flow
|
|
248
248
|
*
|
|
249
249
|
* This function uses `getAuth` from `home-assistant-js-websocket` which:
|
|
250
|
-
* 1.
|
|
250
|
+
* 1. Redirects the whole page to the Home Assistant authorization page
|
|
251
251
|
* 2. Waits for user authorization
|
|
252
|
-
* 3.
|
|
253
|
-
* 4.
|
|
252
|
+
* 3. On return, HA redirects back to the app URL with `?auth_callback=1&code=…`
|
|
253
|
+
* 4. Exchanges the authorization code for tokens and returns an Auth object
|
|
254
254
|
*
|
|
255
255
|
* Note: For a complete OAuth flow, you typically need to:
|
|
256
256
|
* - Set up a redirect URI endpoint in your app
|
|
257
257
|
* - Handle the OAuth callback with the authorization code
|
|
258
258
|
* - Use `getAuthCode` callback to provide the code
|
|
259
259
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
260
|
+
* In browser environments `getAuth` handles this automatically via a full-page
|
|
261
|
+
* redirect (not a popup); native platforms supply `getAuthCode` instead.
|
|
262
262
|
*
|
|
263
263
|
* @param options - OAuth options
|
|
264
264
|
* @returns Authenticated Auth object
|
|
@@ -1491,10 +1491,11 @@ function resolveIcon(entity$1, registry) {
|
|
|
1491
1491
|
*/
|
|
1492
1492
|
function buildEntityView(entity$1, registry) {
|
|
1493
1493
|
const domain = extractDomain(entity$1.entity_id);
|
|
1494
|
-
const
|
|
1494
|
+
const attributes = entity$1.attributes ?? {};
|
|
1495
|
+
const friendlyName = attributes.friendly_name ?? registry?.name ?? entity$1.entity_id;
|
|
1495
1496
|
const name = registry?.name ?? friendlyName;
|
|
1496
1497
|
const iconResolution = resolveIcon(entity$1, registry);
|
|
1497
|
-
const { device_class: _dc, unit_of_measurement: _uom, friendly_name: _fn, icon: _icon,...restAttributes } =
|
|
1498
|
+
const { device_class: _dc, unit_of_measurement: _uom, friendly_name: _fn, icon: _icon,...restAttributes } = attributes;
|
|
1498
1499
|
let areaId = registry?.area_id ?? null;
|
|
1499
1500
|
if (areaId === null && registry?.device_id) {
|
|
1500
1501
|
const device = state.devices[registry.device_id];
|
|
@@ -1508,9 +1509,9 @@ function buildEntityView(entity$1, registry) {
|
|
|
1508
1509
|
lastChanged: new Date(entity$1.last_changed),
|
|
1509
1510
|
lastUpdated: new Date(entity$1.last_updated),
|
|
1510
1511
|
context: {
|
|
1511
|
-
id: entity$1.context
|
|
1512
|
-
parentId: entity$1.context
|
|
1513
|
-
userId: entity$1.context
|
|
1512
|
+
id: entity$1.context?.id ?? "",
|
|
1513
|
+
parentId: entity$1.context?.parent_id ?? null,
|
|
1514
|
+
userId: entity$1.context?.user_id ?? null
|
|
1514
1515
|
},
|
|
1515
1516
|
name,
|
|
1516
1517
|
friendlyName,
|
|
@@ -1525,9 +1526,9 @@ function buildEntityView(entity$1, registry) {
|
|
|
1525
1526
|
entityCategory: registry?.entity_category ?? null,
|
|
1526
1527
|
labels: registry?.labels ?? [],
|
|
1527
1528
|
aliases: registry?.aliases ?? [],
|
|
1528
|
-
deviceClass: registry?.device_class ??
|
|
1529
|
-
unitOfMeasurement: registry?.unit_of_measurement ??
|
|
1530
|
-
supportedFeatures: registry?.supported_features ??
|
|
1529
|
+
deviceClass: registry?.device_class ?? attributes.device_class ?? null,
|
|
1530
|
+
unitOfMeasurement: registry?.unit_of_measurement ?? attributes.unit_of_measurement ?? null,
|
|
1531
|
+
supportedFeatures: registry?.supported_features ?? attributes.supported_features
|
|
1531
1532
|
};
|
|
1532
1533
|
}
|
|
1533
1534
|
/**
|
|
@@ -2190,7 +2191,8 @@ const DEMO_HLS_STREAM = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8";
|
|
|
2190
2191
|
* Returns a public demo HLS stream when not connected.
|
|
2191
2192
|
*/
|
|
2192
2193
|
async function fetchStream(entityId, format = "hls") {
|
|
2193
|
-
if (
|
|
2194
|
+
if (isDemoMode()) return format === "hls" ? DEMO_HLS_STREAM : null;
|
|
2195
|
+
if (!state.conn) return null;
|
|
2194
2196
|
const response = await sendCommand({
|
|
2195
2197
|
type: "camera/stream",
|
|
2196
2198
|
entity_id: entityId,
|
package/dist/testing/index.js
CHANGED