@camstack/addon-import-alexa 0.2.170 → 0.2.171

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/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.171] - 2026-10-30
11
+
12
+ ### Changed
13
+
14
+ - Notification targets pick a broker and list their options inside the add/edit form; the page-wide Find devices dialog is gone
15
+
10
16
  ## [0.2.170] - 2026-10-30
11
17
 
12
18
  ### Changed
package/dist/addon.js CHANGED
@@ -57379,6 +57379,12 @@ var TerminalInstanceInfoSchema = object({
57379
57379
  instanceId: string(),
57380
57380
  cameraStableId: string(),
57381
57381
  nodeId: string(),
57382
+ /**
57383
+ * False while the node the instance is bound to is not in the cluster
57384
+ * topology (forgotten, renamed, re-paired). The instance is kept; the
57385
+ * operator re-points it with `updateInstance({ targetNodeId })`.
57386
+ */
57387
+ nodeInCluster: boolean(),
57382
57388
  profileId: string(),
57383
57389
  profileLabel: string(),
57384
57390
  name: string(),
@@ -57428,6 +57434,8 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
57428
57434
  auth: "admin"
57429
57435
  }), method(object({
57430
57436
  instanceId: string().min(1),
57437
+ /** Re-point the instance to another node; the profile must exist there. */
57438
+ targetNodeId: string().min(1).max(256).optional(),
57431
57439
  name: string().trim().min(1).max(160).optional(),
57432
57440
  executable: string().max(1024).optional(),
57433
57441
  args: array(string().max(2048)).max(64).optional(),
@@ -137868,8 +137876,17 @@ var AlexaBrokerRegistry = class {
137868
137876
  }
137869
137877
  };
137870
137878
  //#endregion
137879
+ //#region src/speak/constants.ts
137880
+ /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
137881
+ var ALEXA_SPEAK_ADDON_ID = "import-alexa";
137882
+ var ALEXA_SPEAK_KIND = "alexa-echo";
137883
+ /** The kind of the Amazon-account broker the Echoes are read from. */
137884
+ var ALEXA_BROKER_KIND = "alexa";
137885
+ var DEVICE_REFRESH_MS = 5 * 6e4;
137886
+ /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
137887
+ var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
137888
+ //#endregion
137871
137889
  //#region src/alexa-broker-provider.ts
137872
- var ALEXA_KIND = "alexa";
137873
137890
  /**
137874
137891
  * Construct the `broker` cap provider for the Alexa import addon. Pure builder.
137875
137892
  * Mirrors `buildEcowittBrokerProvider`. The cookie + refresh token are secrets:
@@ -137887,7 +137904,7 @@ function buildAlexaBrokerProvider(deps) {
137887
137904
  });
137888
137905
  return {
137889
137906
  list: async ({ kind }) => {
137890
- if (kind && kind !== ALEXA_KIND) return [];
137907
+ if (kind && kind !== "alexa") return [];
137891
137908
  return registry.list().map((info) => ({
137892
137909
  ...info,
137893
137910
  addonId: ownerAddonId
@@ -137903,7 +137920,7 @@ function buildAlexaBrokerProvider(deps) {
137903
137920
  listProviders: async () => [{
137904
137921
  addonId: ownerAddonId,
137905
137922
  kinds: [{
137906
- kind: ALEXA_KIND,
137923
+ kind: ALEXA_BROKER_KIND,
137907
137924
  label: "Alexa",
137908
137925
  signIn: {
137909
137926
  basePath: "/addon/import-alexa/login",
@@ -137912,7 +137929,7 @@ function buildAlexaBrokerProvider(deps) {
137912
137929
  }]
137913
137930
  }],
137914
137931
  add: async ({ kind, name, settings }) => {
137915
- if (kind !== ALEXA_KIND) throw new Error(`import-alexa: only kind '${ALEXA_KIND}' is handled here (got '${kind}')`);
137932
+ if (kind !== "alexa") throw new Error(`import-alexa: only kind '${ALEXA_BROKER_KIND}' is handled here (got '${kind}')`);
137916
137933
  const entry = await registry.createEntry(name, settingsToAlexaConnection(settings));
137917
137934
  await persistBrokers([...getBrokers(), entry]);
137918
137935
  return { id: entry.id };
@@ -137970,11 +137987,11 @@ function buildAlexaBrokerProvider(deps) {
137970
137987
  return entry ? redact(entry) : null;
137971
137988
  },
137972
137989
  getSettingsSchema: async ({ kind }) => {
137973
- if (kind !== ALEXA_KIND) return null;
137990
+ if (kind !== "alexa") return null;
137974
137991
  return buildConnectionFormSchema();
137975
137992
  },
137976
137993
  testSettings: async ({ kind, settings }) => {
137977
- if (kind !== ALEXA_KIND) return {
137994
+ if (kind !== "alexa") return {
137978
137995
  ok: false,
137979
137996
  error: `unsupported kind: ${kind}`
137980
137997
  };
@@ -139193,14 +139210,6 @@ var BrokerEcho = class {
139193
139210
  }
139194
139211
  };
139195
139212
  //#endregion
139196
- //#region src/speak/constants.ts
139197
- /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
139198
- var ALEXA_SPEAK_ADDON_ID = "import-alexa";
139199
- var ALEXA_SPEAK_KIND = "alexa-echo";
139200
- var DEVICE_REFRESH_MS = 5 * 6e4;
139201
- /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
139202
- var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
139203
- //#endregion
139204
139213
  //#region src/speak/rate-limit.ts
139205
139214
  /**
139206
139215
  * Per-Echo admission. A burst is dropped here, not queued: a queue would be
@@ -139351,50 +139360,37 @@ function echoOptionLabel(device) {
139351
139360
  if (kind.length > 0) parts.push(kind);
139352
139361
  return parts.join(" · ");
139353
139362
  }
139354
- function echoField(devices, showWhen) {
139355
- return {
139356
- type: "select",
139357
- key: "echoId",
139358
- label: "Echo",
139359
- labelKey: "import-alexa:echoTarget.s_echo.echoId.label",
139360
- description: devices.length === 0 ? "No Echo found yet. Sign the Amazon account in on Integrations, Alexa." : void 0,
139361
- descriptionKey: devices.length === 0 ? "import-alexa:echoTarget.s_echo.echoId.empty" : void 0,
139362
- options: devices.map((device) => ({
139363
- value: device.id,
139364
- label: echoOptionLabel(device)
139365
- })),
139366
- ...showWhen === void 0 ? {} : { showWhen }
139367
- };
139368
- }
139369
139363
  /**
139370
139364
  * The two pickers are not `required`: a required select shows its first option
139371
139365
  * while the form still holds no value, so a save would drop the choice. Left
139372
139366
  * optional they open on "Select", and `upsertTarget` refuses an empty pick.
139373
139367
  *
139374
- * The Echo picker is built from the accounts' own device lists on every call,
139375
- * so a new Echo shows up without a deploy. With one account there is no account
139376
- * field; with several, each account's Echoes appear when that account is chosen.
139368
+ * The account is a broker pick (existing, or created in place); the Echo list
139369
+ * is fetched from that account, so a new Echo shows up without a deploy.
139377
139370
  */
139378
- function configSchema(brokers) {
139379
- const first = brokers[0];
139371
+ function configSchema() {
139380
139372
  return { sections: [{
139381
139373
  id: "echo",
139382
139374
  title: "Echo",
139383
139375
  titleKey: "import-alexa:echoTarget.s_echo.title",
139384
139376
  fields: [
139385
- ...brokers.length > 1 ? [{
139386
- type: "select",
139377
+ ...[{
139378
+ type: "broker-select",
139387
139379
  key: "brokerId",
139388
139380
  label: "Amazon account",
139389
139381
  labelKey: "import-alexa:echoTarget.s_echo.brokerId.label",
139390
- options: brokers.map((broker) => ({
139391
- value: broker.id,
139392
- label: broker.name
139393
- }))
139394
- }, ...brokers.map((broker) => echoField(broker.devices, {
139395
- field: "brokerId",
139396
- equals: broker.id
139397
- }))] : [echoField(first?.devices ?? [])],
139382
+ brokerKind: ALEXA_BROKER_KIND
139383
+ }, {
139384
+ type: "select",
139385
+ key: "echoId",
139386
+ label: "Echo",
139387
+ labelKey: "import-alexa:echoTarget.s_echo.echoId.label",
139388
+ options: [],
139389
+ discoverOptions: {
139390
+ kind: ALEXA_SPEAK_KIND,
139391
+ dependsOn: ["brokerId"]
139392
+ }
139393
+ }],
139398
139394
  {
139399
139395
  type: "select",
139400
139396
  key: "mode",
@@ -139428,8 +139424,8 @@ function configSchema(brokers) {
139428
139424
  ]
139429
139425
  }] };
139430
139426
  }
139431
- /** The Alexa target kind as the Add target form sees it, for the accounts as they are now. */
139432
- function alexaTargetKind(brokers) {
139427
+ /** The Alexa target kind as the Add target form sees it. */
139428
+ function alexaTargetKind() {
139433
139429
  return {
139434
139430
  kind: ALEXA_SPEAK_KIND,
139435
139431
  label: "Alexa",
@@ -139439,7 +139435,7 @@ function alexaTargetKind(brokers) {
139439
139435
  addonId: ALEXA_SPEAK_ADDON_ID,
139440
139436
  supportsDiscovery: true,
139441
139437
  destinationKeys: ["brokerId", "echoId"],
139442
- configSchema: configSchema(brokers),
139438
+ configSchema: configSchema(),
139443
139439
  deliveryParams: DELIVERY_PARAMS,
139444
139440
  caps: {
139445
139441
  attachments: {
@@ -139701,7 +139697,7 @@ function createAlexaNotificationProvider(ports) {
139701
139697
  return deliveries;
139702
139698
  },
139703
139699
  async listTargetKinds() {
139704
- return [alexaTargetKind(ports.brokers())];
139700
+ return [alexaTargetKind()];
139705
139701
  },
139706
139702
  async listTargets() {
139707
139703
  return [...targets];
@@ -139711,7 +139707,20 @@ function createAlexaNotificationProvider(ports) {
139711
139707
  if (found === void 0) throw new Error(`target not found: ${input.targetId}`);
139712
139708
  return found;
139713
139709
  },
139714
- async discoverTargets() {
139710
+ async discoverTargets(input) {
139711
+ const named = readString(input.config ?? {}, "brokerId");
139712
+ if (named.length > 0) {
139713
+ const owner = ports.brokers().find((broker) => broker.id === named);
139714
+ return (owner?.devices ?? []).map((device) => ({
139715
+ ...discoveredFrom(owner ?? {
139716
+ id: named,
139717
+ name: named,
139718
+ ready: false,
139719
+ devices: []
139720
+ }, device),
139721
+ suggestedName: echoOptionLabel(device)
139722
+ }));
139723
+ }
139715
139724
  const taken = new Set(targets.flatMap((row) => {
139716
139725
  const owner = brokerFor(row.config);
139717
139726
  return owner === void 0 ? [] : [`${owner.id}/${readString(row.config, "echoId")}`];
package/dist/addon.mjs CHANGED
@@ -57377,6 +57377,12 @@ var TerminalInstanceInfoSchema = object({
57377
57377
  instanceId: string(),
57378
57378
  cameraStableId: string(),
57379
57379
  nodeId: string(),
57380
+ /**
57381
+ * False while the node the instance is bound to is not in the cluster
57382
+ * topology (forgotten, renamed, re-paired). The instance is kept; the
57383
+ * operator re-points it with `updateInstance({ targetNodeId })`.
57384
+ */
57385
+ nodeInCluster: boolean(),
57380
57386
  profileId: string(),
57381
57387
  profileLabel: string(),
57382
57388
  name: string(),
@@ -57426,6 +57432,8 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
57426
57432
  auth: "admin"
57427
57433
  }), method(object({
57428
57434
  instanceId: string().min(1),
57435
+ /** Re-point the instance to another node; the profile must exist there. */
57436
+ targetNodeId: string().min(1).max(256).optional(),
57429
57437
  name: string().trim().min(1).max(160).optional(),
57430
57438
  executable: string().max(1024).optional(),
57431
57439
  args: array(string().max(2048)).max(64).optional(),
@@ -137866,8 +137874,17 @@ var AlexaBrokerRegistry = class {
137866
137874
  }
137867
137875
  };
137868
137876
  //#endregion
137877
+ //#region src/speak/constants.ts
137878
+ /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
137879
+ var ALEXA_SPEAK_ADDON_ID = "import-alexa";
137880
+ var ALEXA_SPEAK_KIND = "alexa-echo";
137881
+ /** The kind of the Amazon-account broker the Echoes are read from. */
137882
+ var ALEXA_BROKER_KIND = "alexa";
137883
+ var DEVICE_REFRESH_MS = 5 * 6e4;
137884
+ /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
137885
+ var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
137886
+ //#endregion
137869
137887
  //#region src/alexa-broker-provider.ts
137870
- var ALEXA_KIND = "alexa";
137871
137888
  /**
137872
137889
  * Construct the `broker` cap provider for the Alexa import addon. Pure builder.
137873
137890
  * Mirrors `buildEcowittBrokerProvider`. The cookie + refresh token are secrets:
@@ -137885,7 +137902,7 @@ function buildAlexaBrokerProvider(deps) {
137885
137902
  });
137886
137903
  return {
137887
137904
  list: async ({ kind }) => {
137888
- if (kind && kind !== ALEXA_KIND) return [];
137905
+ if (kind && kind !== "alexa") return [];
137889
137906
  return registry.list().map((info) => ({
137890
137907
  ...info,
137891
137908
  addonId: ownerAddonId
@@ -137901,7 +137918,7 @@ function buildAlexaBrokerProvider(deps) {
137901
137918
  listProviders: async () => [{
137902
137919
  addonId: ownerAddonId,
137903
137920
  kinds: [{
137904
- kind: ALEXA_KIND,
137921
+ kind: ALEXA_BROKER_KIND,
137905
137922
  label: "Alexa",
137906
137923
  signIn: {
137907
137924
  basePath: "/addon/import-alexa/login",
@@ -137910,7 +137927,7 @@ function buildAlexaBrokerProvider(deps) {
137910
137927
  }]
137911
137928
  }],
137912
137929
  add: async ({ kind, name, settings }) => {
137913
- if (kind !== ALEXA_KIND) throw new Error(`import-alexa: only kind '${ALEXA_KIND}' is handled here (got '${kind}')`);
137930
+ if (kind !== "alexa") throw new Error(`import-alexa: only kind '${ALEXA_BROKER_KIND}' is handled here (got '${kind}')`);
137914
137931
  const entry = await registry.createEntry(name, settingsToAlexaConnection(settings));
137915
137932
  await persistBrokers([...getBrokers(), entry]);
137916
137933
  return { id: entry.id };
@@ -137968,11 +137985,11 @@ function buildAlexaBrokerProvider(deps) {
137968
137985
  return entry ? redact(entry) : null;
137969
137986
  },
137970
137987
  getSettingsSchema: async ({ kind }) => {
137971
- if (kind !== ALEXA_KIND) return null;
137988
+ if (kind !== "alexa") return null;
137972
137989
  return buildConnectionFormSchema();
137973
137990
  },
137974
137991
  testSettings: async ({ kind, settings }) => {
137975
- if (kind !== ALEXA_KIND) return {
137992
+ if (kind !== "alexa") return {
137976
137993
  ok: false,
137977
137994
  error: `unsupported kind: ${kind}`
137978
137995
  };
@@ -139191,14 +139208,6 @@ var BrokerEcho = class {
139191
139208
  }
139192
139209
  };
139193
139210
  //#endregion
139194
- //#region src/speak/constants.ts
139195
- /** Echo speech limits and the worker restart bound (same window as CrashSupervisor). */
139196
- var ALEXA_SPEAK_ADDON_ID = "import-alexa";
139197
- var ALEXA_SPEAK_KIND = "alexa-echo";
139198
- var DEVICE_REFRESH_MS = 5 * 6e4;
139199
- /** Where the addon serves the Alexa mark; `listTargetKinds` stamps it as `iconUrl`. */
139200
- var ALEXA_SPEAK_ICON_URL = `/addon/${ALEXA_SPEAK_ADDON_ID}/icons/${ALEXA_SPEAK_KIND}`;
139201
- //#endregion
139202
139211
  //#region src/speak/rate-limit.ts
139203
139212
  /**
139204
139213
  * Per-Echo admission. A burst is dropped here, not queued: a queue would be
@@ -139349,50 +139358,37 @@ function echoOptionLabel(device) {
139349
139358
  if (kind.length > 0) parts.push(kind);
139350
139359
  return parts.join(" · ");
139351
139360
  }
139352
- function echoField(devices, showWhen) {
139353
- return {
139354
- type: "select",
139355
- key: "echoId",
139356
- label: "Echo",
139357
- labelKey: "import-alexa:echoTarget.s_echo.echoId.label",
139358
- description: devices.length === 0 ? "No Echo found yet. Sign the Amazon account in on Integrations, Alexa." : void 0,
139359
- descriptionKey: devices.length === 0 ? "import-alexa:echoTarget.s_echo.echoId.empty" : void 0,
139360
- options: devices.map((device) => ({
139361
- value: device.id,
139362
- label: echoOptionLabel(device)
139363
- })),
139364
- ...showWhen === void 0 ? {} : { showWhen }
139365
- };
139366
- }
139367
139361
  /**
139368
139362
  * The two pickers are not `required`: a required select shows its first option
139369
139363
  * while the form still holds no value, so a save would drop the choice. Left
139370
139364
  * optional they open on "Select", and `upsertTarget` refuses an empty pick.
139371
139365
  *
139372
- * The Echo picker is built from the accounts' own device lists on every call,
139373
- * so a new Echo shows up without a deploy. With one account there is no account
139374
- * field; with several, each account's Echoes appear when that account is chosen.
139366
+ * The account is a broker pick (existing, or created in place); the Echo list
139367
+ * is fetched from that account, so a new Echo shows up without a deploy.
139375
139368
  */
139376
- function configSchema(brokers) {
139377
- const first = brokers[0];
139369
+ function configSchema() {
139378
139370
  return { sections: [{
139379
139371
  id: "echo",
139380
139372
  title: "Echo",
139381
139373
  titleKey: "import-alexa:echoTarget.s_echo.title",
139382
139374
  fields: [
139383
- ...brokers.length > 1 ? [{
139384
- type: "select",
139375
+ ...[{
139376
+ type: "broker-select",
139385
139377
  key: "brokerId",
139386
139378
  label: "Amazon account",
139387
139379
  labelKey: "import-alexa:echoTarget.s_echo.brokerId.label",
139388
- options: brokers.map((broker) => ({
139389
- value: broker.id,
139390
- label: broker.name
139391
- }))
139392
- }, ...brokers.map((broker) => echoField(broker.devices, {
139393
- field: "brokerId",
139394
- equals: broker.id
139395
- }))] : [echoField(first?.devices ?? [])],
139380
+ brokerKind: ALEXA_BROKER_KIND
139381
+ }, {
139382
+ type: "select",
139383
+ key: "echoId",
139384
+ label: "Echo",
139385
+ labelKey: "import-alexa:echoTarget.s_echo.echoId.label",
139386
+ options: [],
139387
+ discoverOptions: {
139388
+ kind: ALEXA_SPEAK_KIND,
139389
+ dependsOn: ["brokerId"]
139390
+ }
139391
+ }],
139396
139392
  {
139397
139393
  type: "select",
139398
139394
  key: "mode",
@@ -139426,8 +139422,8 @@ function configSchema(brokers) {
139426
139422
  ]
139427
139423
  }] };
139428
139424
  }
139429
- /** The Alexa target kind as the Add target form sees it, for the accounts as they are now. */
139430
- function alexaTargetKind(brokers) {
139425
+ /** The Alexa target kind as the Add target form sees it. */
139426
+ function alexaTargetKind() {
139431
139427
  return {
139432
139428
  kind: ALEXA_SPEAK_KIND,
139433
139429
  label: "Alexa",
@@ -139437,7 +139433,7 @@ function alexaTargetKind(brokers) {
139437
139433
  addonId: ALEXA_SPEAK_ADDON_ID,
139438
139434
  supportsDiscovery: true,
139439
139435
  destinationKeys: ["brokerId", "echoId"],
139440
- configSchema: configSchema(brokers),
139436
+ configSchema: configSchema(),
139441
139437
  deliveryParams: DELIVERY_PARAMS,
139442
139438
  caps: {
139443
139439
  attachments: {
@@ -139699,7 +139695,7 @@ function createAlexaNotificationProvider(ports) {
139699
139695
  return deliveries;
139700
139696
  },
139701
139697
  async listTargetKinds() {
139702
- return [alexaTargetKind(ports.brokers())];
139698
+ return [alexaTargetKind()];
139703
139699
  },
139704
139700
  async listTargets() {
139705
139701
  return [...targets];
@@ -139709,7 +139705,20 @@ function createAlexaNotificationProvider(ports) {
139709
139705
  if (found === void 0) throw new Error(`target not found: ${input.targetId}`);
139710
139706
  return found;
139711
139707
  },
139712
- async discoverTargets() {
139708
+ async discoverTargets(input) {
139709
+ const named = readString(input.config ?? {}, "brokerId");
139710
+ if (named.length > 0) {
139711
+ const owner = ports.brokers().find((broker) => broker.id === named);
139712
+ return (owner?.devices ?? []).map((device) => ({
139713
+ ...discoveredFrom(owner ?? {
139714
+ id: named,
139715
+ name: named,
139716
+ ready: false,
139717
+ devices: []
139718
+ }, device),
139719
+ suggestedName: echoOptionLabel(device)
139720
+ }));
139721
+ }
139713
139722
  const taken = new Set(targets.flatMap((row) => {
139714
139723
  const owner = brokerFor(row.config);
139715
139724
  return owner === void 0 ? [] : [`${owner.id}/${readString(row.config, "echoId")}`];
package/i18n/en.json CHANGED
@@ -14,7 +14,6 @@
14
14
  "echoTarget.s_echo.title": "Echo",
15
15
  "echoTarget.s_echo.brokerId.label": "Amazon account",
16
16
  "echoTarget.s_echo.echoId.label": "Echo",
17
- "echoTarget.s_echo.echoId.empty": "No Echo found yet. Sign the Amazon account in on Integrations, Alexa.",
18
17
  "echoTarget.s_echo.mode.label": "Mode",
19
18
  "echoTarget.s_echo.volume.label": "Volume",
20
19
  "echoTarget.s_echo.respectDnd.label": "Respect Do Not Disturb",
package/i18n/it.json CHANGED
@@ -14,7 +14,6 @@
14
14
  "echoTarget.s_echo.title": "Echo",
15
15
  "echoTarget.s_echo.brokerId.label": "Account Amazon",
16
16
  "echoTarget.s_echo.echoId.label": "Echo",
17
- "echoTarget.s_echo.echoId.empty": "Nessun Echo trovato. Accedi con l'account Amazon da Integrazioni, Alexa.",
18
17
  "echoTarget.s_echo.mode.label": "Modalità",
19
18
  "echoTarget.s_echo.volume.label": "Volume",
20
19
  "echoTarget.s_echo.respectDnd.label": "Rispetta Non disturbare",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-import-alexa",
3
- "version": "0.2.170",
3
+ "version": "0.2.171",
4
4
  "description": "Alexa device-import provider for CamStack — imports the smart-home devices in a user's Alexa account via the unofficial alexa-remote2 cookie/token client (the inverse of the Alexa exporter)",
5
5
  "keywords": [
6
6
  "camstack",