@camstack/addon-waste-collections 0.1.1 → 0.1.3
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/waste-collections.addon.js +305 -89
- package/dist/waste-collections.addon.mjs +305 -89
- package/package.json +1 -1
|
@@ -18084,14 +18084,14 @@ var LlmProfileSchema = object({
|
|
|
18084
18084
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
18085
18085
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
18086
18086
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
18087
|
-
var ConfigSchemaPassthrough$
|
|
18087
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
18088
18088
|
var LlmProfileKindDescriptorSchema = object({
|
|
18089
18089
|
kind: LlmProfileKindSchema,
|
|
18090
18090
|
label: string(),
|
|
18091
18091
|
icon: string(),
|
|
18092
18092
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
18093
18093
|
addonId: string(),
|
|
18094
|
-
configSchema: ConfigSchemaPassthrough$
|
|
18094
|
+
configSchema: ConfigSchemaPassthrough$3
|
|
18095
18095
|
});
|
|
18096
18096
|
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
18097
18097
|
var LlmDefaultSchema = object({
|
|
@@ -19212,7 +19212,7 @@ var TargetKindCapsSchema = object({
|
|
|
19212
19212
|
* the union is large and not meant for runtime validation here; the exported
|
|
19213
19213
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
19214
19214
|
*/
|
|
19215
|
-
var ConfigSchemaPassthrough = unknown();
|
|
19215
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
19216
19216
|
var TargetKindSchema = object({
|
|
19217
19217
|
kind: string(),
|
|
19218
19218
|
label: string(),
|
|
@@ -19249,7 +19249,7 @@ var TargetKindSchema = object({
|
|
|
19249
19249
|
* its raster path, which is the safe default for an unknown image.
|
|
19250
19250
|
*/
|
|
19251
19251
|
iconMediaType: string().optional(),
|
|
19252
|
-
configSchema: ConfigSchemaPassthrough,
|
|
19252
|
+
configSchema: ConfigSchemaPassthrough$2,
|
|
19253
19253
|
/**
|
|
19254
19254
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
19255
19255
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -22548,6 +22548,61 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
22548
22548
|
});
|
|
22549
22549
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
22550
22550
|
/**
|
|
22551
|
+
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
22552
|
+
*
|
|
22553
|
+
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
22554
|
+
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
22555
|
+
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
22556
|
+
* immutable after create.
|
|
22557
|
+
*/
|
|
22558
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
22559
|
+
var OidcConnectionSchema = object({
|
|
22560
|
+
id: string().min(1),
|
|
22561
|
+
displayName: string(),
|
|
22562
|
+
icon: string(),
|
|
22563
|
+
issuerUrl: string(),
|
|
22564
|
+
clientId: string(),
|
|
22565
|
+
/** Client id + secret present — never the secret value. */
|
|
22566
|
+
configured: boolean(),
|
|
22567
|
+
/**
|
|
22568
|
+
* Values for the connection form schema. `clientSecret` is a keychain
|
|
22569
|
+
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
22570
|
+
*/
|
|
22571
|
+
formValues: record(string(), unknown()).optional()
|
|
22572
|
+
});
|
|
22573
|
+
/**
|
|
22574
|
+
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
22575
|
+
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
22576
|
+
*/
|
|
22577
|
+
var OidcConnectionUpdateInputSchema = object({
|
|
22578
|
+
id: string().min(1),
|
|
22579
|
+
displayName: string().min(1),
|
|
22580
|
+
icon: string().optional(),
|
|
22581
|
+
issuerUrl: string().optional(),
|
|
22582
|
+
clientId: string().optional(),
|
|
22583
|
+
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
22584
|
+
redirectUri: string().optional(),
|
|
22585
|
+
scopes: string().optional(),
|
|
22586
|
+
usernameClaim: _enum([
|
|
22587
|
+
"preferred_username",
|
|
22588
|
+
"email",
|
|
22589
|
+
"sub"
|
|
22590
|
+
]).optional()
|
|
22591
|
+
});
|
|
22592
|
+
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
22593
|
+
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
22594
|
+
displayName: string().min(1)
|
|
22595
|
+
}), object({ id: string() }), {
|
|
22596
|
+
kind: "mutation",
|
|
22597
|
+
auth: "admin"
|
|
22598
|
+
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
22599
|
+
kind: "mutation",
|
|
22600
|
+
auth: "admin"
|
|
22601
|
+
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
22602
|
+
kind: "mutation",
|
|
22603
|
+
auth: "admin"
|
|
22604
|
+
});
|
|
22605
|
+
/**
|
|
22551
22606
|
* What started a camera's onboard motion (D672). `camera` is the camera's own
|
|
22552
22607
|
* firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
|
|
22553
22608
|
* wire means the camera: only the camera-sensors bridge ever says `sensor`.
|
|
@@ -31572,6 +31627,7 @@ var calendarSourceCapability = {
|
|
|
31572
31627
|
scope: "system",
|
|
31573
31628
|
mode: "collection",
|
|
31574
31629
|
internal: true,
|
|
31630
|
+
providerKind: "calendar",
|
|
31575
31631
|
methods: {
|
|
31576
31632
|
listCalendars: method(_void(), array(CalendarInfoSchema).readonly(), { auth: "admin" }),
|
|
31577
31633
|
listEvents: method(CalendarListEventsInputSchema, array(CalendarEventSchema).readonly(), { auth: "admin" })
|
|
@@ -35053,6 +35109,7 @@ var OsdNamedTemplateSchema = object({
|
|
|
35053
35109
|
});
|
|
35054
35110
|
/** Where a slot's value comes from. */
|
|
35055
35111
|
var OsdSourceSchema = discriminatedUnion("kind", [
|
|
35112
|
+
object({ kind: literal("none") }),
|
|
35056
35113
|
object({
|
|
35057
35114
|
kind: literal("static"),
|
|
35058
35115
|
text: string().max(64)
|
|
@@ -39376,6 +39433,12 @@ var TrainingProviderDefaultsSchema = object({
|
|
|
39376
39433
|
/** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
|
|
39377
39434
|
failedDatasetGraceHours: number().nonnegative()
|
|
39378
39435
|
});
|
|
39436
|
+
/**
|
|
39437
|
+
* Entry form schema for a provider type — same seam as notification
|
|
39438
|
+
* `TargetKind.configSchema` (`z.unknown()` at the wire; callers tighten to
|
|
39439
|
+
* {@link ConfigUISchema}). Drives `ConfigFormBuilder` on Training → Providers.
|
|
39440
|
+
*/
|
|
39441
|
+
var ConfigSchemaPassthrough = unknown();
|
|
39379
39442
|
/** One creatable provider type, for the Add-entry dialog. */
|
|
39380
39443
|
var TrainingProviderTypeSchema = object({
|
|
39381
39444
|
typeId: string(),
|
|
@@ -39383,7 +39446,13 @@ var TrainingProviderTypeSchema = object({
|
|
|
39383
39446
|
/** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
|
|
39384
39447
|
iconAsset: string(),
|
|
39385
39448
|
/** Owning `training-provider` addon — collection selector for createEntry. */
|
|
39386
|
-
addonId: string()
|
|
39449
|
+
addonId: string(),
|
|
39450
|
+
/**
|
|
39451
|
+
* Fields for one entry of this type (credentials + defaults). Absent on a
|
|
39452
|
+
* type that only needs a name. Password fields are keychain-backed on the
|
|
39453
|
+
* provider addon (`secretStatePaths`), not D634 plaintext.
|
|
39454
|
+
*/
|
|
39455
|
+
configSchema: ConfigSchemaPassthrough.optional()
|
|
39387
39456
|
});
|
|
39388
39457
|
/** One configured account / workspace under a provider type. */
|
|
39389
39458
|
var TrainingProviderEntrySchema = object({
|
|
@@ -39413,7 +39482,30 @@ var TrainingProviderDescriptorSchema = object({
|
|
|
39413
39482
|
dataResidencyNote: string(),
|
|
39414
39483
|
/** Credentials are present — never the values. */
|
|
39415
39484
|
configured: boolean(),
|
|
39416
|
-
defaults: TrainingProviderDefaultsSchema
|
|
39485
|
+
defaults: TrainingProviderDefaultsSchema,
|
|
39486
|
+
/**
|
|
39487
|
+
* Values for {@link TrainingProviderTypeSchema}'s `configSchema` — password
|
|
39488
|
+
* fields are keychain {@link SecretFieldView} or `null` / `''` (create),
|
|
39489
|
+
* never plaintext. Same hydrate contract as settings forms (D789).
|
|
39490
|
+
*/
|
|
39491
|
+
formValues: record(string(), unknown()).optional()
|
|
39492
|
+
});
|
|
39493
|
+
/**
|
|
39494
|
+
* Patch one entry's name, credentials and defaults. Pin with owning `{ addonId }`.
|
|
39495
|
+
* An omitted or empty `tokenSecret` means KEEP the stored secret (D787).
|
|
39496
|
+
* `{ $secretClear: true }` clears it.
|
|
39497
|
+
*/
|
|
39498
|
+
var TrainingProviderUpdateEntryInputSchema = object({
|
|
39499
|
+
entryId: string().min(1),
|
|
39500
|
+
name: string().min(1),
|
|
39501
|
+
tokenId: string().optional(),
|
|
39502
|
+
tokenSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
39503
|
+
environment: string().optional(),
|
|
39504
|
+
gpuType: string().optional(),
|
|
39505
|
+
region: string().optional(),
|
|
39506
|
+
costCapUsd: number().nonnegative().optional(),
|
|
39507
|
+
maxRuntimeSec: number().int().positive().optional(),
|
|
39508
|
+
failedDatasetGraceHours: number().nonnegative().optional()
|
|
39417
39509
|
});
|
|
39418
39510
|
var TrainingCredentialCheckSchema = object({
|
|
39419
39511
|
ok: boolean(),
|
|
@@ -39483,6 +39575,9 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
|
|
|
39483
39575
|
}), method(object({ entryId: string().min(1) }), object({ ok: literal(true) }), {
|
|
39484
39576
|
kind: "mutation",
|
|
39485
39577
|
auth: "admin"
|
|
39578
|
+
}), method(TrainingProviderUpdateEntryInputSchema, object({ ok: literal(true) }), {
|
|
39579
|
+
kind: "mutation",
|
|
39580
|
+
auth: "admin"
|
|
39486
39581
|
}), method(object({ entryId: string().min(1) }), TrainingCredentialCheckSchema, {
|
|
39487
39582
|
kind: "mutation",
|
|
39488
39583
|
auth: "admin",
|
|
@@ -43858,6 +43953,36 @@ Object.freeze({
|
|
|
43858
43953
|
addonId: null,
|
|
43859
43954
|
access: "view"
|
|
43860
43955
|
},
|
|
43956
|
+
"oidcConnections.connectionFormSchema": {
|
|
43957
|
+
capName: "oidc-connections",
|
|
43958
|
+
capScope: "system",
|
|
43959
|
+
addonId: null,
|
|
43960
|
+
access: "view"
|
|
43961
|
+
},
|
|
43962
|
+
"oidcConnections.createConnection": {
|
|
43963
|
+
capName: "oidc-connections",
|
|
43964
|
+
capScope: "system",
|
|
43965
|
+
addonId: null,
|
|
43966
|
+
access: "create"
|
|
43967
|
+
},
|
|
43968
|
+
"oidcConnections.deleteConnection": {
|
|
43969
|
+
capName: "oidc-connections",
|
|
43970
|
+
capScope: "system",
|
|
43971
|
+
addonId: null,
|
|
43972
|
+
access: "delete"
|
|
43973
|
+
},
|
|
43974
|
+
"oidcConnections.listConnections": {
|
|
43975
|
+
capName: "oidc-connections",
|
|
43976
|
+
capScope: "system",
|
|
43977
|
+
addonId: null,
|
|
43978
|
+
access: "view"
|
|
43979
|
+
},
|
|
43980
|
+
"oidcConnections.updateConnection": {
|
|
43981
|
+
capName: "oidc-connections",
|
|
43982
|
+
capScope: "system",
|
|
43983
|
+
addonId: null,
|
|
43984
|
+
access: "create"
|
|
43985
|
+
},
|
|
43861
43986
|
"osd.getStatus": {
|
|
43862
43987
|
capName: "osd",
|
|
43863
43988
|
capScope: "device",
|
|
@@ -47044,6 +47169,12 @@ Object.freeze({
|
|
|
47044
47169
|
addonId: null,
|
|
47045
47170
|
access: "create"
|
|
47046
47171
|
},
|
|
47172
|
+
"trainingProvider.updateEntry": {
|
|
47173
|
+
capName: "training-provider",
|
|
47174
|
+
capScope: "system",
|
|
47175
|
+
addonId: null,
|
|
47176
|
+
access: "create"
|
|
47177
|
+
},
|
|
47047
47178
|
"trainingProvider.uploadDataset": {
|
|
47048
47179
|
capName: "training-provider",
|
|
47049
47180
|
capScope: "system",
|
|
@@ -51589,6 +51720,7 @@ Object.freeze({
|
|
|
51589
51720
|
});
|
|
51590
51721
|
Object.freeze({
|
|
51591
51722
|
"broker": "broker",
|
|
51723
|
+
"calendar-source": "calendar",
|
|
51592
51724
|
"device-export": "device-export",
|
|
51593
51725
|
"llm": "ai",
|
|
51594
51726
|
"mesh-network": "mesh",
|
|
@@ -52628,18 +52760,6 @@ DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
|
52628
52760
|
var MB = 1024 * 1024;
|
|
52629
52761
|
1024 * MB, 3072 * MB;
|
|
52630
52762
|
//#endregion
|
|
52631
|
-
//#region src/custom-actions.ts
|
|
52632
|
-
var wasteCollectionsActions = defineCustomActions({ fetchNow: customAction(object({
|
|
52633
|
-
/** When set, refresh only this configured source id; otherwise all enabled. */
|
|
52634
|
-
sourceId: string().min(1).optional() }).optional(), object({
|
|
52635
|
-
ok: literal(true),
|
|
52636
|
-
refreshed: array(object({
|
|
52637
|
-
sourceId: string(),
|
|
52638
|
-
entryCount: number().int(),
|
|
52639
|
-
error: string().optional()
|
|
52640
|
-
}))
|
|
52641
|
-
}), { kind: "mutation" }) });
|
|
52642
|
-
//#endregion
|
|
52643
52763
|
//#region src/waste-types.ts
|
|
52644
52764
|
var WasteSourceConfigSchema = object({
|
|
52645
52765
|
id: string().min(1),
|
|
@@ -52680,6 +52800,54 @@ var WasteFetchResultSchema = discriminatedUnion("ok", [object({
|
|
|
52680
52800
|
error: string()
|
|
52681
52801
|
})]);
|
|
52682
52802
|
//#endregion
|
|
52803
|
+
//#region src/custom-actions.ts
|
|
52804
|
+
var SourceIdSchema = string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug");
|
|
52805
|
+
var WasteSourceRowSchema = WasteSourceConfigSchema.extend({ formValues: record(string(), unknown()).optional() });
|
|
52806
|
+
var wasteCollectionsActions = defineCustomActions({
|
|
52807
|
+
fetchNow: customAction(object({
|
|
52808
|
+
/** When set, refresh only this configured source id; otherwise all enabled. */
|
|
52809
|
+
sourceId: string().min(1).optional() }).optional(), object({
|
|
52810
|
+
ok: literal(true),
|
|
52811
|
+
refreshed: array(object({
|
|
52812
|
+
sourceId: string(),
|
|
52813
|
+
entryCount: number().int(),
|
|
52814
|
+
error: string().optional()
|
|
52815
|
+
}))
|
|
52816
|
+
}), {
|
|
52817
|
+
kind: "mutation",
|
|
52818
|
+
auth: "admin"
|
|
52819
|
+
}),
|
|
52820
|
+
listSources: customAction(object({}), array(WasteSourceRowSchema).readonly(), {
|
|
52821
|
+
kind: "mutation",
|
|
52822
|
+
auth: "admin"
|
|
52823
|
+
}),
|
|
52824
|
+
sourceFormSchema: customAction(object({}), unknown(), {
|
|
52825
|
+
kind: "mutation",
|
|
52826
|
+
auth: "admin"
|
|
52827
|
+
}),
|
|
52828
|
+
createSource: customAction(object({
|
|
52829
|
+
id: SourceIdSchema,
|
|
52830
|
+
label: string().min(1)
|
|
52831
|
+
}), object({ id: string() }), {
|
|
52832
|
+
kind: "mutation",
|
|
52833
|
+
auth: "admin"
|
|
52834
|
+
}),
|
|
52835
|
+
updateSource: customAction(object({
|
|
52836
|
+
id: string().min(1),
|
|
52837
|
+
label: string().min(1),
|
|
52838
|
+
module: string().min(1).optional(),
|
|
52839
|
+
argsJson: string().optional(),
|
|
52840
|
+
enabled: boolean().optional()
|
|
52841
|
+
}), object({ ok: literal(true) }), {
|
|
52842
|
+
kind: "mutation",
|
|
52843
|
+
auth: "admin"
|
|
52844
|
+
}),
|
|
52845
|
+
deleteSource: customAction(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
52846
|
+
kind: "mutation",
|
|
52847
|
+
auth: "admin"
|
|
52848
|
+
})
|
|
52849
|
+
});
|
|
52850
|
+
//#endregion
|
|
52683
52851
|
//#region src/fetch-source.ts
|
|
52684
52852
|
/**
|
|
52685
52853
|
* Spawn the embedded Python bridge once per source fetch.
|
|
@@ -52824,11 +52992,56 @@ function createWasteCalendarSource(deps) {
|
|
|
52824
52992
|
};
|
|
52825
52993
|
}
|
|
52826
52994
|
//#endregion
|
|
52995
|
+
//#region src/waste-source-schema.ts
|
|
52996
|
+
var WASTE_SOURCE_FORM_SCHEMA = { sections: [{
|
|
52997
|
+
id: "source",
|
|
52998
|
+
title: "Source",
|
|
52999
|
+
columns: 2,
|
|
53000
|
+
fields: [
|
|
53001
|
+
{
|
|
53002
|
+
type: "text",
|
|
53003
|
+
key: "label",
|
|
53004
|
+
label: "Label",
|
|
53005
|
+
description: "Shown under System → Calendars.",
|
|
53006
|
+
default: "New source"
|
|
53007
|
+
},
|
|
53008
|
+
{
|
|
53009
|
+
type: "text",
|
|
53010
|
+
key: "module",
|
|
53011
|
+
label: "Source module",
|
|
53012
|
+
description: "HACS provider name, e.g. `ics`, `junker_app`, `abfallnavi_de`.",
|
|
53013
|
+
default: "ics"
|
|
53014
|
+
},
|
|
53015
|
+
{
|
|
53016
|
+
type: "textarea",
|
|
53017
|
+
key: "argsJson",
|
|
53018
|
+
label: "Args (JSON object)",
|
|
53019
|
+
description: "Constructor kwargs for the HACS source, e.g. `{\"url\":\"https://…/calendar.ics\"}`.",
|
|
53020
|
+
rows: 4,
|
|
53021
|
+
default: "{}",
|
|
53022
|
+
span: 2
|
|
53023
|
+
},
|
|
53024
|
+
{
|
|
53025
|
+
type: "boolean",
|
|
53026
|
+
key: "enabled",
|
|
53027
|
+
label: "Enabled",
|
|
53028
|
+
default: true
|
|
53029
|
+
}
|
|
53030
|
+
]
|
|
53031
|
+
}] };
|
|
53032
|
+
var WASTE_SOURCE_CREATE_VALUES = {
|
|
53033
|
+
label: "New source",
|
|
53034
|
+
module: "ics",
|
|
53035
|
+
argsJson: "{}",
|
|
53036
|
+
enabled: true
|
|
53037
|
+
};
|
|
53038
|
+
//#endregion
|
|
52827
53039
|
//#region src/waste-collections.addon.ts
|
|
52828
53040
|
/**
|
|
52829
53041
|
* Waste Collections — hub-only calendar-source over the vendored HACS
|
|
52830
53042
|
* `waste_collection_schedule` Python core (full source catalog).
|
|
52831
53043
|
*/
|
|
53044
|
+
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
52832
53045
|
var DAY_MS = 1440 * 60 * 1e3;
|
|
52833
53046
|
var WasteCollectionsAddon = class extends BaseAddon {
|
|
52834
53047
|
pythonPath = null;
|
|
@@ -52876,7 +53089,14 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
52876
53089
|
provider
|
|
52877
53090
|
}],
|
|
52878
53091
|
customActions: wasteCollectionsActions,
|
|
52879
|
-
actionHandlers: {
|
|
53092
|
+
actionHandlers: {
|
|
53093
|
+
fetchNow: async (input) => this.fetchNow(input),
|
|
53094
|
+
listSources: async () => this.listSources(),
|
|
53095
|
+
sourceFormSchema: async () => WASTE_SOURCE_FORM_SCHEMA,
|
|
53096
|
+
createSource: async (input) => this.createSource(input),
|
|
53097
|
+
updateSource: async (input) => this.updateSource(input),
|
|
53098
|
+
deleteSource: async (input) => this.deleteSource(input)
|
|
53099
|
+
}
|
|
52880
53100
|
};
|
|
52881
53101
|
}
|
|
52882
53102
|
async onConfigChanged() {
|
|
@@ -52885,6 +53105,49 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
52885
53105
|
this.persistCache();
|
|
52886
53106
|
this.refreshAll("config-changed");
|
|
52887
53107
|
}
|
|
53108
|
+
listSources() {
|
|
53109
|
+
return this.config.sources.map((s) => ({
|
|
53110
|
+
...s,
|
|
53111
|
+
formValues: {
|
|
53112
|
+
label: s.label,
|
|
53113
|
+
module: s.module,
|
|
53114
|
+
argsJson: s.argsJson,
|
|
53115
|
+
enabled: s.enabled
|
|
53116
|
+
}
|
|
53117
|
+
}));
|
|
53118
|
+
}
|
|
53119
|
+
async createSource(input) {
|
|
53120
|
+
if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
|
|
53121
|
+
if (this.config.sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' already exists`);
|
|
53122
|
+
const row = WasteSourceConfigSchema.parse({
|
|
53123
|
+
...WASTE_SOURCE_CREATE_VALUES,
|
|
53124
|
+
id: input.id,
|
|
53125
|
+
label: input.label
|
|
53126
|
+
});
|
|
53127
|
+
await this.updateGlobalSettings({ sources: [...this.config.sources, row] });
|
|
53128
|
+
return { id: input.id };
|
|
53129
|
+
}
|
|
53130
|
+
async updateSource(input) {
|
|
53131
|
+
const idx = this.config.sources.findIndex((s) => s.id === input.id);
|
|
53132
|
+
if (idx < 0) throw new Error(`source '${input.id}' not found`);
|
|
53133
|
+
const prev = this.config.sources[idx];
|
|
53134
|
+
const next = WasteSourceConfigSchema.parse({
|
|
53135
|
+
id: prev.id,
|
|
53136
|
+
label: input.label,
|
|
53137
|
+
module: input.module ?? prev.module,
|
|
53138
|
+
argsJson: input.argsJson ?? prev.argsJson,
|
|
53139
|
+
enabled: input.enabled ?? prev.enabled
|
|
53140
|
+
});
|
|
53141
|
+
parseSourceArgs(next);
|
|
53142
|
+
const sources = this.config.sources.map((s, i) => i === idx ? next : s);
|
|
53143
|
+
await this.updateGlobalSettings({ sources });
|
|
53144
|
+
return { ok: true };
|
|
53145
|
+
}
|
|
53146
|
+
async deleteSource(input) {
|
|
53147
|
+
if (!this.config.sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' not found`);
|
|
53148
|
+
await this.updateGlobalSettings({ sources: this.config.sources.filter((s) => s.id !== input.id) });
|
|
53149
|
+
return { ok: true };
|
|
53150
|
+
}
|
|
52888
53151
|
async fetchNow(input) {
|
|
52889
53152
|
const targets = this.config.sources.filter((s) => {
|
|
52890
53153
|
if (!s.enabled) return false;
|
|
@@ -52987,76 +53250,29 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
52987
53250
|
}
|
|
52988
53251
|
globalSettingsSchema() {
|
|
52989
53252
|
return this.schema({ sections: [{
|
|
52990
|
-
id: "
|
|
52991
|
-
title: "
|
|
52992
|
-
description: "
|
|
52993
|
-
columns:
|
|
52994
|
-
fields: [
|
|
52995
|
-
|
|
52996
|
-
|
|
52997
|
-
|
|
52998
|
-
|
|
52999
|
-
|
|
53000
|
-
|
|
53001
|
-
|
|
53002
|
-
|
|
53003
|
-
|
|
53004
|
-
|
|
53005
|
-
|
|
53006
|
-
|
|
53007
|
-
|
|
53008
|
-
|
|
53009
|
-
|
|
53010
|
-
|
|
53011
|
-
|
|
53012
|
-
|
|
53013
|
-
type: "text",
|
|
53014
|
-
key: "module",
|
|
53015
|
-
label: "Source module",
|
|
53016
|
-
description: "e.g. `ics`, `junker_app`, `abfallnavi_de`."
|
|
53017
|
-
},
|
|
53018
|
-
{
|
|
53019
|
-
type: "textarea",
|
|
53020
|
-
key: "argsJson",
|
|
53021
|
-
label: "Args (JSON object)",
|
|
53022
|
-
description: "Constructor kwargs for the HACS source, e.g. `{\"url\":\"https://…/calendar.ics\"}`.",
|
|
53023
|
-
rows: 4
|
|
53024
|
-
},
|
|
53025
|
-
{
|
|
53026
|
-
type: "boolean",
|
|
53027
|
-
key: "enabled",
|
|
53028
|
-
label: "Enabled"
|
|
53029
|
-
}
|
|
53030
|
-
],
|
|
53031
|
-
defaultItem: {
|
|
53032
|
-
id: "",
|
|
53033
|
-
label: "New source",
|
|
53034
|
-
module: "ics",
|
|
53035
|
-
argsJson: "{}",
|
|
53036
|
-
enabled: true
|
|
53037
|
-
}
|
|
53038
|
-
},
|
|
53039
|
-
{
|
|
53040
|
-
type: "number",
|
|
53041
|
-
key: "fetchHour",
|
|
53042
|
-
label: "Daily fetch hour (local)",
|
|
53043
|
-
min: 0,
|
|
53044
|
-
max: 23,
|
|
53045
|
-
step: 1,
|
|
53046
|
-
description: "Informational — the addon refreshes about once per day from boot; use Fetch now to force."
|
|
53047
|
-
},
|
|
53048
|
-
{
|
|
53049
|
-
type: "addon-action-button",
|
|
53050
|
-
key: "_fetchNow",
|
|
53051
|
-
label: "Refresh",
|
|
53052
|
-
buttonLabel: "Fetch now",
|
|
53053
|
-
description: "Refresh every enabled source immediately.",
|
|
53054
|
-
addonId: "waste-collections",
|
|
53055
|
-
action: "fetchNow",
|
|
53056
|
-
variant: "primary",
|
|
53057
|
-
paramsStatic: {}
|
|
53058
|
-
}
|
|
53059
|
-
]
|
|
53253
|
+
id: "refresh",
|
|
53254
|
+
title: "Refresh",
|
|
53255
|
+
description: "Sources are managed in the table above (Add / Edit). Each source becomes one calendar under System → Calendars.",
|
|
53256
|
+
columns: 2,
|
|
53257
|
+
fields: [{
|
|
53258
|
+
type: "number",
|
|
53259
|
+
key: "fetchHour",
|
|
53260
|
+
label: "Daily fetch hour (local)",
|
|
53261
|
+
min: 0,
|
|
53262
|
+
max: 23,
|
|
53263
|
+
step: 1,
|
|
53264
|
+
description: "Informational — the addon refreshes about once per day from boot; use Fetch now to force."
|
|
53265
|
+
}, {
|
|
53266
|
+
type: "addon-action-button",
|
|
53267
|
+
key: "_fetchNow",
|
|
53268
|
+
label: "Refresh",
|
|
53269
|
+
buttonLabel: "Fetch now",
|
|
53270
|
+
description: "Refresh every enabled source immediately.",
|
|
53271
|
+
addonId: "waste-collections",
|
|
53272
|
+
action: "fetchNow",
|
|
53273
|
+
variant: "primary",
|
|
53274
|
+
paramsStatic: {}
|
|
53275
|
+
}]
|
|
53060
53276
|
}] });
|
|
53061
53277
|
}
|
|
53062
53278
|
};
|
|
@@ -18080,14 +18080,14 @@ var LlmProfileSchema = object({
|
|
|
18080
18080
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
18081
18081
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
18082
18082
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
18083
|
-
var ConfigSchemaPassthrough$
|
|
18083
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
18084
18084
|
var LlmProfileKindDescriptorSchema = object({
|
|
18085
18085
|
kind: LlmProfileKindSchema,
|
|
18086
18086
|
label: string(),
|
|
18087
18087
|
icon: string(),
|
|
18088
18088
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
18089
18089
|
addonId: string(),
|
|
18090
|
-
configSchema: ConfigSchemaPassthrough$
|
|
18090
|
+
configSchema: ConfigSchemaPassthrough$3
|
|
18091
18091
|
});
|
|
18092
18092
|
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
18093
18093
|
var LlmDefaultSchema = object({
|
|
@@ -19208,7 +19208,7 @@ var TargetKindCapsSchema = object({
|
|
|
19208
19208
|
* the union is large and not meant for runtime validation here; the exported
|
|
19209
19209
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
19210
19210
|
*/
|
|
19211
|
-
var ConfigSchemaPassthrough = unknown();
|
|
19211
|
+
var ConfigSchemaPassthrough$2 = unknown();
|
|
19212
19212
|
var TargetKindSchema = object({
|
|
19213
19213
|
kind: string(),
|
|
19214
19214
|
label: string(),
|
|
@@ -19245,7 +19245,7 @@ var TargetKindSchema = object({
|
|
|
19245
19245
|
* its raster path, which is the safe default for an unknown image.
|
|
19246
19246
|
*/
|
|
19247
19247
|
iconMediaType: string().optional(),
|
|
19248
|
-
configSchema: ConfigSchemaPassthrough,
|
|
19248
|
+
configSchema: ConfigSchemaPassthrough$2,
|
|
19249
19249
|
/**
|
|
19250
19250
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
19251
19251
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -22544,6 +22544,61 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
22544
22544
|
});
|
|
22545
22545
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
22546
22546
|
/**
|
|
22547
|
+
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
22548
|
+
*
|
|
22549
|
+
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
22550
|
+
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
22551
|
+
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
22552
|
+
* immutable after create.
|
|
22553
|
+
*/
|
|
22554
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
22555
|
+
var OidcConnectionSchema = object({
|
|
22556
|
+
id: string().min(1),
|
|
22557
|
+
displayName: string(),
|
|
22558
|
+
icon: string(),
|
|
22559
|
+
issuerUrl: string(),
|
|
22560
|
+
clientId: string(),
|
|
22561
|
+
/** Client id + secret present — never the secret value. */
|
|
22562
|
+
configured: boolean(),
|
|
22563
|
+
/**
|
|
22564
|
+
* Values for the connection form schema. `clientSecret` is a keychain
|
|
22565
|
+
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
22566
|
+
*/
|
|
22567
|
+
formValues: record(string(), unknown()).optional()
|
|
22568
|
+
});
|
|
22569
|
+
/**
|
|
22570
|
+
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
22571
|
+
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
22572
|
+
*/
|
|
22573
|
+
var OidcConnectionUpdateInputSchema = object({
|
|
22574
|
+
id: string().min(1),
|
|
22575
|
+
displayName: string().min(1),
|
|
22576
|
+
icon: string().optional(),
|
|
22577
|
+
issuerUrl: string().optional(),
|
|
22578
|
+
clientId: string().optional(),
|
|
22579
|
+
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
22580
|
+
redirectUri: string().optional(),
|
|
22581
|
+
scopes: string().optional(),
|
|
22582
|
+
usernameClaim: _enum([
|
|
22583
|
+
"preferred_username",
|
|
22584
|
+
"email",
|
|
22585
|
+
"sub"
|
|
22586
|
+
]).optional()
|
|
22587
|
+
});
|
|
22588
|
+
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
22589
|
+
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
22590
|
+
displayName: string().min(1)
|
|
22591
|
+
}), object({ id: string() }), {
|
|
22592
|
+
kind: "mutation",
|
|
22593
|
+
auth: "admin"
|
|
22594
|
+
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
22595
|
+
kind: "mutation",
|
|
22596
|
+
auth: "admin"
|
|
22597
|
+
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
22598
|
+
kind: "mutation",
|
|
22599
|
+
auth: "admin"
|
|
22600
|
+
});
|
|
22601
|
+
/**
|
|
22547
22602
|
* What started a camera's onboard motion (D672). `camera` is the camera's own
|
|
22548
22603
|
* firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
|
|
22549
22604
|
* wire means the camera: only the camera-sensors bridge ever says `sensor`.
|
|
@@ -31568,6 +31623,7 @@ var calendarSourceCapability = {
|
|
|
31568
31623
|
scope: "system",
|
|
31569
31624
|
mode: "collection",
|
|
31570
31625
|
internal: true,
|
|
31626
|
+
providerKind: "calendar",
|
|
31571
31627
|
methods: {
|
|
31572
31628
|
listCalendars: method(_void(), array(CalendarInfoSchema).readonly(), { auth: "admin" }),
|
|
31573
31629
|
listEvents: method(CalendarListEventsInputSchema, array(CalendarEventSchema).readonly(), { auth: "admin" })
|
|
@@ -35049,6 +35105,7 @@ var OsdNamedTemplateSchema = object({
|
|
|
35049
35105
|
});
|
|
35050
35106
|
/** Where a slot's value comes from. */
|
|
35051
35107
|
var OsdSourceSchema = discriminatedUnion("kind", [
|
|
35108
|
+
object({ kind: literal("none") }),
|
|
35052
35109
|
object({
|
|
35053
35110
|
kind: literal("static"),
|
|
35054
35111
|
text: string().max(64)
|
|
@@ -39372,6 +39429,12 @@ var TrainingProviderDefaultsSchema = object({
|
|
|
39372
39429
|
/** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
|
|
39373
39430
|
failedDatasetGraceHours: number().nonnegative()
|
|
39374
39431
|
});
|
|
39432
|
+
/**
|
|
39433
|
+
* Entry form schema for a provider type — same seam as notification
|
|
39434
|
+
* `TargetKind.configSchema` (`z.unknown()` at the wire; callers tighten to
|
|
39435
|
+
* {@link ConfigUISchema}). Drives `ConfigFormBuilder` on Training → Providers.
|
|
39436
|
+
*/
|
|
39437
|
+
var ConfigSchemaPassthrough = unknown();
|
|
39375
39438
|
/** One creatable provider type, for the Add-entry dialog. */
|
|
39376
39439
|
var TrainingProviderTypeSchema = object({
|
|
39377
39440
|
typeId: string(),
|
|
@@ -39379,7 +39442,13 @@ var TrainingProviderTypeSchema = object({
|
|
|
39379
39442
|
/** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
|
|
39380
39443
|
iconAsset: string(),
|
|
39381
39444
|
/** Owning `training-provider` addon — collection selector for createEntry. */
|
|
39382
|
-
addonId: string()
|
|
39445
|
+
addonId: string(),
|
|
39446
|
+
/**
|
|
39447
|
+
* Fields for one entry of this type (credentials + defaults). Absent on a
|
|
39448
|
+
* type that only needs a name. Password fields are keychain-backed on the
|
|
39449
|
+
* provider addon (`secretStatePaths`), not D634 plaintext.
|
|
39450
|
+
*/
|
|
39451
|
+
configSchema: ConfigSchemaPassthrough.optional()
|
|
39383
39452
|
});
|
|
39384
39453
|
/** One configured account / workspace under a provider type. */
|
|
39385
39454
|
var TrainingProviderEntrySchema = object({
|
|
@@ -39409,7 +39478,30 @@ var TrainingProviderDescriptorSchema = object({
|
|
|
39409
39478
|
dataResidencyNote: string(),
|
|
39410
39479
|
/** Credentials are present — never the values. */
|
|
39411
39480
|
configured: boolean(),
|
|
39412
|
-
defaults: TrainingProviderDefaultsSchema
|
|
39481
|
+
defaults: TrainingProviderDefaultsSchema,
|
|
39482
|
+
/**
|
|
39483
|
+
* Values for {@link TrainingProviderTypeSchema}'s `configSchema` — password
|
|
39484
|
+
* fields are keychain {@link SecretFieldView} or `null` / `''` (create),
|
|
39485
|
+
* never plaintext. Same hydrate contract as settings forms (D789).
|
|
39486
|
+
*/
|
|
39487
|
+
formValues: record(string(), unknown()).optional()
|
|
39488
|
+
});
|
|
39489
|
+
/**
|
|
39490
|
+
* Patch one entry's name, credentials and defaults. Pin with owning `{ addonId }`.
|
|
39491
|
+
* An omitted or empty `tokenSecret` means KEEP the stored secret (D787).
|
|
39492
|
+
* `{ $secretClear: true }` clears it.
|
|
39493
|
+
*/
|
|
39494
|
+
var TrainingProviderUpdateEntryInputSchema = object({
|
|
39495
|
+
entryId: string().min(1),
|
|
39496
|
+
name: string().min(1),
|
|
39497
|
+
tokenId: string().optional(),
|
|
39498
|
+
tokenSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
39499
|
+
environment: string().optional(),
|
|
39500
|
+
gpuType: string().optional(),
|
|
39501
|
+
region: string().optional(),
|
|
39502
|
+
costCapUsd: number().nonnegative().optional(),
|
|
39503
|
+
maxRuntimeSec: number().int().positive().optional(),
|
|
39504
|
+
failedDatasetGraceHours: number().nonnegative().optional()
|
|
39413
39505
|
});
|
|
39414
39506
|
var TrainingCredentialCheckSchema = object({
|
|
39415
39507
|
ok: boolean(),
|
|
@@ -39479,6 +39571,9 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
|
|
|
39479
39571
|
}), method(object({ entryId: string().min(1) }), object({ ok: literal(true) }), {
|
|
39480
39572
|
kind: "mutation",
|
|
39481
39573
|
auth: "admin"
|
|
39574
|
+
}), method(TrainingProviderUpdateEntryInputSchema, object({ ok: literal(true) }), {
|
|
39575
|
+
kind: "mutation",
|
|
39576
|
+
auth: "admin"
|
|
39482
39577
|
}), method(object({ entryId: string().min(1) }), TrainingCredentialCheckSchema, {
|
|
39483
39578
|
kind: "mutation",
|
|
39484
39579
|
auth: "admin",
|
|
@@ -43854,6 +43949,36 @@ Object.freeze({
|
|
|
43854
43949
|
addonId: null,
|
|
43855
43950
|
access: "view"
|
|
43856
43951
|
},
|
|
43952
|
+
"oidcConnections.connectionFormSchema": {
|
|
43953
|
+
capName: "oidc-connections",
|
|
43954
|
+
capScope: "system",
|
|
43955
|
+
addonId: null,
|
|
43956
|
+
access: "view"
|
|
43957
|
+
},
|
|
43958
|
+
"oidcConnections.createConnection": {
|
|
43959
|
+
capName: "oidc-connections",
|
|
43960
|
+
capScope: "system",
|
|
43961
|
+
addonId: null,
|
|
43962
|
+
access: "create"
|
|
43963
|
+
},
|
|
43964
|
+
"oidcConnections.deleteConnection": {
|
|
43965
|
+
capName: "oidc-connections",
|
|
43966
|
+
capScope: "system",
|
|
43967
|
+
addonId: null,
|
|
43968
|
+
access: "delete"
|
|
43969
|
+
},
|
|
43970
|
+
"oidcConnections.listConnections": {
|
|
43971
|
+
capName: "oidc-connections",
|
|
43972
|
+
capScope: "system",
|
|
43973
|
+
addonId: null,
|
|
43974
|
+
access: "view"
|
|
43975
|
+
},
|
|
43976
|
+
"oidcConnections.updateConnection": {
|
|
43977
|
+
capName: "oidc-connections",
|
|
43978
|
+
capScope: "system",
|
|
43979
|
+
addonId: null,
|
|
43980
|
+
access: "create"
|
|
43981
|
+
},
|
|
43857
43982
|
"osd.getStatus": {
|
|
43858
43983
|
capName: "osd",
|
|
43859
43984
|
capScope: "device",
|
|
@@ -47040,6 +47165,12 @@ Object.freeze({
|
|
|
47040
47165
|
addonId: null,
|
|
47041
47166
|
access: "create"
|
|
47042
47167
|
},
|
|
47168
|
+
"trainingProvider.updateEntry": {
|
|
47169
|
+
capName: "training-provider",
|
|
47170
|
+
capScope: "system",
|
|
47171
|
+
addonId: null,
|
|
47172
|
+
access: "create"
|
|
47173
|
+
},
|
|
47043
47174
|
"trainingProvider.uploadDataset": {
|
|
47044
47175
|
capName: "training-provider",
|
|
47045
47176
|
capScope: "system",
|
|
@@ -51585,6 +51716,7 @@ Object.freeze({
|
|
|
51585
51716
|
});
|
|
51586
51717
|
Object.freeze({
|
|
51587
51718
|
"broker": "broker",
|
|
51719
|
+
"calendar-source": "calendar",
|
|
51588
51720
|
"device-export": "device-export",
|
|
51589
51721
|
"llm": "ai",
|
|
51590
51722
|
"mesh-network": "mesh",
|
|
@@ -52624,18 +52756,6 @@ DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
|
52624
52756
|
var MB = 1024 * 1024;
|
|
52625
52757
|
1024 * MB, 3072 * MB;
|
|
52626
52758
|
//#endregion
|
|
52627
|
-
//#region src/custom-actions.ts
|
|
52628
|
-
var wasteCollectionsActions = defineCustomActions({ fetchNow: customAction(object({
|
|
52629
|
-
/** When set, refresh only this configured source id; otherwise all enabled. */
|
|
52630
|
-
sourceId: string().min(1).optional() }).optional(), object({
|
|
52631
|
-
ok: literal(true),
|
|
52632
|
-
refreshed: array(object({
|
|
52633
|
-
sourceId: string(),
|
|
52634
|
-
entryCount: number().int(),
|
|
52635
|
-
error: string().optional()
|
|
52636
|
-
}))
|
|
52637
|
-
}), { kind: "mutation" }) });
|
|
52638
|
-
//#endregion
|
|
52639
52759
|
//#region src/waste-types.ts
|
|
52640
52760
|
var WasteSourceConfigSchema = object({
|
|
52641
52761
|
id: string().min(1),
|
|
@@ -52676,6 +52796,54 @@ var WasteFetchResultSchema = discriminatedUnion("ok", [object({
|
|
|
52676
52796
|
error: string()
|
|
52677
52797
|
})]);
|
|
52678
52798
|
//#endregion
|
|
52799
|
+
//#region src/custom-actions.ts
|
|
52800
|
+
var SourceIdSchema = string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug");
|
|
52801
|
+
var WasteSourceRowSchema = WasteSourceConfigSchema.extend({ formValues: record(string(), unknown()).optional() });
|
|
52802
|
+
var wasteCollectionsActions = defineCustomActions({
|
|
52803
|
+
fetchNow: customAction(object({
|
|
52804
|
+
/** When set, refresh only this configured source id; otherwise all enabled. */
|
|
52805
|
+
sourceId: string().min(1).optional() }).optional(), object({
|
|
52806
|
+
ok: literal(true),
|
|
52807
|
+
refreshed: array(object({
|
|
52808
|
+
sourceId: string(),
|
|
52809
|
+
entryCount: number().int(),
|
|
52810
|
+
error: string().optional()
|
|
52811
|
+
}))
|
|
52812
|
+
}), {
|
|
52813
|
+
kind: "mutation",
|
|
52814
|
+
auth: "admin"
|
|
52815
|
+
}),
|
|
52816
|
+
listSources: customAction(object({}), array(WasteSourceRowSchema).readonly(), {
|
|
52817
|
+
kind: "mutation",
|
|
52818
|
+
auth: "admin"
|
|
52819
|
+
}),
|
|
52820
|
+
sourceFormSchema: customAction(object({}), unknown(), {
|
|
52821
|
+
kind: "mutation",
|
|
52822
|
+
auth: "admin"
|
|
52823
|
+
}),
|
|
52824
|
+
createSource: customAction(object({
|
|
52825
|
+
id: SourceIdSchema,
|
|
52826
|
+
label: string().min(1)
|
|
52827
|
+
}), object({ id: string() }), {
|
|
52828
|
+
kind: "mutation",
|
|
52829
|
+
auth: "admin"
|
|
52830
|
+
}),
|
|
52831
|
+
updateSource: customAction(object({
|
|
52832
|
+
id: string().min(1),
|
|
52833
|
+
label: string().min(1),
|
|
52834
|
+
module: string().min(1).optional(),
|
|
52835
|
+
argsJson: string().optional(),
|
|
52836
|
+
enabled: boolean().optional()
|
|
52837
|
+
}), object({ ok: literal(true) }), {
|
|
52838
|
+
kind: "mutation",
|
|
52839
|
+
auth: "admin"
|
|
52840
|
+
}),
|
|
52841
|
+
deleteSource: customAction(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
52842
|
+
kind: "mutation",
|
|
52843
|
+
auth: "admin"
|
|
52844
|
+
})
|
|
52845
|
+
});
|
|
52846
|
+
//#endregion
|
|
52679
52847
|
//#region src/fetch-source.ts
|
|
52680
52848
|
/**
|
|
52681
52849
|
* Spawn the embedded Python bridge once per source fetch.
|
|
@@ -52820,11 +52988,56 @@ function createWasteCalendarSource(deps) {
|
|
|
52820
52988
|
};
|
|
52821
52989
|
}
|
|
52822
52990
|
//#endregion
|
|
52991
|
+
//#region src/waste-source-schema.ts
|
|
52992
|
+
var WASTE_SOURCE_FORM_SCHEMA = { sections: [{
|
|
52993
|
+
id: "source",
|
|
52994
|
+
title: "Source",
|
|
52995
|
+
columns: 2,
|
|
52996
|
+
fields: [
|
|
52997
|
+
{
|
|
52998
|
+
type: "text",
|
|
52999
|
+
key: "label",
|
|
53000
|
+
label: "Label",
|
|
53001
|
+
description: "Shown under System → Calendars.",
|
|
53002
|
+
default: "New source"
|
|
53003
|
+
},
|
|
53004
|
+
{
|
|
53005
|
+
type: "text",
|
|
53006
|
+
key: "module",
|
|
53007
|
+
label: "Source module",
|
|
53008
|
+
description: "HACS provider name, e.g. `ics`, `junker_app`, `abfallnavi_de`.",
|
|
53009
|
+
default: "ics"
|
|
53010
|
+
},
|
|
53011
|
+
{
|
|
53012
|
+
type: "textarea",
|
|
53013
|
+
key: "argsJson",
|
|
53014
|
+
label: "Args (JSON object)",
|
|
53015
|
+
description: "Constructor kwargs for the HACS source, e.g. `{\"url\":\"https://…/calendar.ics\"}`.",
|
|
53016
|
+
rows: 4,
|
|
53017
|
+
default: "{}",
|
|
53018
|
+
span: 2
|
|
53019
|
+
},
|
|
53020
|
+
{
|
|
53021
|
+
type: "boolean",
|
|
53022
|
+
key: "enabled",
|
|
53023
|
+
label: "Enabled",
|
|
53024
|
+
default: true
|
|
53025
|
+
}
|
|
53026
|
+
]
|
|
53027
|
+
}] };
|
|
53028
|
+
var WASTE_SOURCE_CREATE_VALUES = {
|
|
53029
|
+
label: "New source",
|
|
53030
|
+
module: "ics",
|
|
53031
|
+
argsJson: "{}",
|
|
53032
|
+
enabled: true
|
|
53033
|
+
};
|
|
53034
|
+
//#endregion
|
|
52823
53035
|
//#region src/waste-collections.addon.ts
|
|
52824
53036
|
/**
|
|
52825
53037
|
* Waste Collections — hub-only calendar-source over the vendored HACS
|
|
52826
53038
|
* `waste_collection_schedule` Python core (full source catalog).
|
|
52827
53039
|
*/
|
|
53040
|
+
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
52828
53041
|
var DAY_MS = 1440 * 60 * 1e3;
|
|
52829
53042
|
var WasteCollectionsAddon = class extends BaseAddon {
|
|
52830
53043
|
pythonPath = null;
|
|
@@ -52872,7 +53085,14 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
52872
53085
|
provider
|
|
52873
53086
|
}],
|
|
52874
53087
|
customActions: wasteCollectionsActions,
|
|
52875
|
-
actionHandlers: {
|
|
53088
|
+
actionHandlers: {
|
|
53089
|
+
fetchNow: async (input) => this.fetchNow(input),
|
|
53090
|
+
listSources: async () => this.listSources(),
|
|
53091
|
+
sourceFormSchema: async () => WASTE_SOURCE_FORM_SCHEMA,
|
|
53092
|
+
createSource: async (input) => this.createSource(input),
|
|
53093
|
+
updateSource: async (input) => this.updateSource(input),
|
|
53094
|
+
deleteSource: async (input) => this.deleteSource(input)
|
|
53095
|
+
}
|
|
52876
53096
|
};
|
|
52877
53097
|
}
|
|
52878
53098
|
async onConfigChanged() {
|
|
@@ -52881,6 +53101,49 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
52881
53101
|
this.persistCache();
|
|
52882
53102
|
this.refreshAll("config-changed");
|
|
52883
53103
|
}
|
|
53104
|
+
listSources() {
|
|
53105
|
+
return this.config.sources.map((s) => ({
|
|
53106
|
+
...s,
|
|
53107
|
+
formValues: {
|
|
53108
|
+
label: s.label,
|
|
53109
|
+
module: s.module,
|
|
53110
|
+
argsJson: s.argsJson,
|
|
53111
|
+
enabled: s.enabled
|
|
53112
|
+
}
|
|
53113
|
+
}));
|
|
53114
|
+
}
|
|
53115
|
+
async createSource(input) {
|
|
53116
|
+
if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
|
|
53117
|
+
if (this.config.sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' already exists`);
|
|
53118
|
+
const row = WasteSourceConfigSchema.parse({
|
|
53119
|
+
...WASTE_SOURCE_CREATE_VALUES,
|
|
53120
|
+
id: input.id,
|
|
53121
|
+
label: input.label
|
|
53122
|
+
});
|
|
53123
|
+
await this.updateGlobalSettings({ sources: [...this.config.sources, row] });
|
|
53124
|
+
return { id: input.id };
|
|
53125
|
+
}
|
|
53126
|
+
async updateSource(input) {
|
|
53127
|
+
const idx = this.config.sources.findIndex((s) => s.id === input.id);
|
|
53128
|
+
if (idx < 0) throw new Error(`source '${input.id}' not found`);
|
|
53129
|
+
const prev = this.config.sources[idx];
|
|
53130
|
+
const next = WasteSourceConfigSchema.parse({
|
|
53131
|
+
id: prev.id,
|
|
53132
|
+
label: input.label,
|
|
53133
|
+
module: input.module ?? prev.module,
|
|
53134
|
+
argsJson: input.argsJson ?? prev.argsJson,
|
|
53135
|
+
enabled: input.enabled ?? prev.enabled
|
|
53136
|
+
});
|
|
53137
|
+
parseSourceArgs(next);
|
|
53138
|
+
const sources = this.config.sources.map((s, i) => i === idx ? next : s);
|
|
53139
|
+
await this.updateGlobalSettings({ sources });
|
|
53140
|
+
return { ok: true };
|
|
53141
|
+
}
|
|
53142
|
+
async deleteSource(input) {
|
|
53143
|
+
if (!this.config.sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' not found`);
|
|
53144
|
+
await this.updateGlobalSettings({ sources: this.config.sources.filter((s) => s.id !== input.id) });
|
|
53145
|
+
return { ok: true };
|
|
53146
|
+
}
|
|
52884
53147
|
async fetchNow(input) {
|
|
52885
53148
|
const targets = this.config.sources.filter((s) => {
|
|
52886
53149
|
if (!s.enabled) return false;
|
|
@@ -52983,76 +53246,29 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
52983
53246
|
}
|
|
52984
53247
|
globalSettingsSchema() {
|
|
52985
53248
|
return this.schema({ sections: [{
|
|
52986
|
-
id: "
|
|
52987
|
-
title: "
|
|
52988
|
-
description: "
|
|
52989
|
-
columns:
|
|
52990
|
-
fields: [
|
|
52991
|
-
|
|
52992
|
-
|
|
52993
|
-
|
|
52994
|
-
|
|
52995
|
-
|
|
52996
|
-
|
|
52997
|
-
|
|
52998
|
-
|
|
52999
|
-
|
|
53000
|
-
|
|
53001
|
-
|
|
53002
|
-
|
|
53003
|
-
|
|
53004
|
-
|
|
53005
|
-
|
|
53006
|
-
|
|
53007
|
-
|
|
53008
|
-
|
|
53009
|
-
type: "text",
|
|
53010
|
-
key: "module",
|
|
53011
|
-
label: "Source module",
|
|
53012
|
-
description: "e.g. `ics`, `junker_app`, `abfallnavi_de`."
|
|
53013
|
-
},
|
|
53014
|
-
{
|
|
53015
|
-
type: "textarea",
|
|
53016
|
-
key: "argsJson",
|
|
53017
|
-
label: "Args (JSON object)",
|
|
53018
|
-
description: "Constructor kwargs for the HACS source, e.g. `{\"url\":\"https://…/calendar.ics\"}`.",
|
|
53019
|
-
rows: 4
|
|
53020
|
-
},
|
|
53021
|
-
{
|
|
53022
|
-
type: "boolean",
|
|
53023
|
-
key: "enabled",
|
|
53024
|
-
label: "Enabled"
|
|
53025
|
-
}
|
|
53026
|
-
],
|
|
53027
|
-
defaultItem: {
|
|
53028
|
-
id: "",
|
|
53029
|
-
label: "New source",
|
|
53030
|
-
module: "ics",
|
|
53031
|
-
argsJson: "{}",
|
|
53032
|
-
enabled: true
|
|
53033
|
-
}
|
|
53034
|
-
},
|
|
53035
|
-
{
|
|
53036
|
-
type: "number",
|
|
53037
|
-
key: "fetchHour",
|
|
53038
|
-
label: "Daily fetch hour (local)",
|
|
53039
|
-
min: 0,
|
|
53040
|
-
max: 23,
|
|
53041
|
-
step: 1,
|
|
53042
|
-
description: "Informational — the addon refreshes about once per day from boot; use Fetch now to force."
|
|
53043
|
-
},
|
|
53044
|
-
{
|
|
53045
|
-
type: "addon-action-button",
|
|
53046
|
-
key: "_fetchNow",
|
|
53047
|
-
label: "Refresh",
|
|
53048
|
-
buttonLabel: "Fetch now",
|
|
53049
|
-
description: "Refresh every enabled source immediately.",
|
|
53050
|
-
addonId: "waste-collections",
|
|
53051
|
-
action: "fetchNow",
|
|
53052
|
-
variant: "primary",
|
|
53053
|
-
paramsStatic: {}
|
|
53054
|
-
}
|
|
53055
|
-
]
|
|
53249
|
+
id: "refresh",
|
|
53250
|
+
title: "Refresh",
|
|
53251
|
+
description: "Sources are managed in the table above (Add / Edit). Each source becomes one calendar under System → Calendars.",
|
|
53252
|
+
columns: 2,
|
|
53253
|
+
fields: [{
|
|
53254
|
+
type: "number",
|
|
53255
|
+
key: "fetchHour",
|
|
53256
|
+
label: "Daily fetch hour (local)",
|
|
53257
|
+
min: 0,
|
|
53258
|
+
max: 23,
|
|
53259
|
+
step: 1,
|
|
53260
|
+
description: "Informational — the addon refreshes about once per day from boot; use Fetch now to force."
|
|
53261
|
+
}, {
|
|
53262
|
+
type: "addon-action-button",
|
|
53263
|
+
key: "_fetchNow",
|
|
53264
|
+
label: "Refresh",
|
|
53265
|
+
buttonLabel: "Fetch now",
|
|
53266
|
+
description: "Refresh every enabled source immediately.",
|
|
53267
|
+
addonId: "waste-collections",
|
|
53268
|
+
action: "fetchNow",
|
|
53269
|
+
variant: "primary",
|
|
53270
|
+
paramsStatic: {}
|
|
53271
|
+
}]
|
|
53056
53272
|
}] });
|
|
53057
53273
|
}
|
|
53058
53274
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-waste-collections",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Waste collection schedules via the vendored HACS waste_collection_schedule Python core — full source catalog as calendar-source.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|