Alexa Skill : Supression massive et rapide des devices-scenes Home Assistant dans Alexa

Salut à tous,

Si comme moi vous avez galéré des heures après vos premiers test d’utilisation du skill pour Alexa (Amazon Alexa Smart Home Skill et que vous vous êtes retrouve avec des centaines de devices et de scène à nettoyer à la main, vous allez adorer ce post !!!

Voici 2 scripts inspirés de cette discussion GitHub qui vous permette de purger tout ce bazare en quelques clics.

ATTENTION, cela n’est à utiliser qu’avec des notions de codes car il n’y a aucune confirmation, vérification ou autre … c’est brute de fonderie et ça delete dur !!!

Dans le code, j’ai commenté le process de supression et vous pourrez l’activer qu’après avoir vérifier le résultat du scan … pas fou non plus :stuck_out_tongue_winking_eye:

Il ne vous faudra rien de plus qu’un navigateur web et moins de 10 minutes

DEVICES :

//
//
// BATCH SCRIPT TO CLEAN ALEXA  DEVICES BY MANUFACTURER and UNREACHABLE STATE
//
//
// 0. Thanks 
/*
	based on git hub : https://github.com/Shereef/Python-Delete-Alexa-Devices/issues/9
	tks to rPraml - Roland Praml  and  challs - Chris Halls and off course all other contributors
*/

// 0.process 
/*
	- (OPTIONNAL) In my case i disconnect my Home assistant skill from Alexa before this batch to avoid conflict
	- open a web browser
	- log to amazon account ( account lin k to alexa...)
	- try the following URLs to get the one who give you a list of devices  :
		https://alexa.amazon.de/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://alexa.amazon.com/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://layla.amazon.com/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://pitangui.amazon.com/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://alexa.amazon.co.jp/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
	- stay on the page that give you Result
	- start dev tools console
	- change the manufactureur bellow (MANUFACTURER_FILTER = ...)
	- copy, paste and execute the whole script in the console
	- if the result look's like what you are looking for , copy/paste the commented delete loop to the console without comments
	- have fun
	
*/

// 1. Filter configuration
const MANUFACTURER_FILTER = "Home Assistant";

// 2. data extraction
devices = await (await fetch('/nexus/v1/graphql', {
  method: 'POST',
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({
    query: `{ 
      endpoints { 
        items { 
          friendlyName
          legacyAppliance { 
            applianceId
            manufacturerName
            connectedVia
          }
          features { 
            name 
            properties { 
              ... on Reachability { reachabilityStatusValue } 
            } 
          } 
        } 
      } 
    }`
  })
})).json();

// 3. Filtering : manufacturerName + UNREACHABLE
toProcess = devices.data.endpoints.items
  .filter(device => {
    const matchesManu = device.legacyAppliance?.manufacturerName === MANUFACTURER_FILTER;

    const reachability = device.features
      .flatMap(f => f.properties)
      .find(p => p?.reachabilityStatusValue !== undefined);
    const isUnreachable = reachability?.reachabilityStatusValue === "UNREACHABLE";

    return matchesManu && isUnreachable;
  })
  .sort((a, b) => a.friendlyName.localeCompare(b.friendlyName))
  .map(device => ({
    friendlyName: device.friendlyName,
    applianceId: device.legacyAppliance.applianceId,
    manufacturerName: device.legacyAppliance.manufacturerName,
    reachabilityStatus: device.features
      .flatMap(f => f.properties)
      .find(p => p?.reachabilityStatusValue !== undefined)
      ?.reachabilityStatusValue ?? "unknown"
  }));

// 4. Result details
console.log(`Found ${toProcess.length} device(s) to process out of ${devices.data.endpoints.items.length} total.`);
toProcess.forEach(d => console.log(` - ${d.friendlyName}`));
console.log("toProcess:", toProcess);


// 5. Deletion batch (commented to avoid delete process before a verification of the scan :)  )
/*
for (const device of toProcess) {
  console.log("Deleting:", device.friendlyName, `(reachability: ${device.reachabilityStatus})`, device.applianceId);
  let result = await fetch(`/api/phoenix/appliance/${encodeURIComponent(device.applianceId)}`, {
    method: "DELETE",
    headers: {
      "Accept": "application/json",
      "Content-Type": "application/json"
    }
  });
  console.log("Result:", result.status, result.statusText);
}
*/

SCENES:

//
//
// BATCH SCRIPT TO CLEAN ALEXA  SCENE BY MANUFACTURER 
// (Alexa scene can include more than scene ... as example, Home Assistant script, scene, automations are identify as scene by Alexa)
//
//
// 0. Thanks 
/*
	based on git hub : https://github.com/Shereef/Python-Delete-Alexa-Devices/issues/9
	tks to rPraml - Roland Praml  and  challs - Chris Halls and off course all other contributors
*/

// 0.process 
/*
	- (OPTIONNAL) In my case i disconnect my Home assistant skill from Alexa before this batch to avoid conflict
	- open a web browser
	- log to amazon account ( account lin k to alexa...)
	- try the following URLs to get the one who give you a list of devices  :
		https://alexa.amazon.de/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://alexa.amazon.com/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://layla.amazon.com/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://pitangui.amazon.com/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
		https://alexa.amazon.co.jp/api/behaviors/entities?skillId=amzn1.ask.1p.smarthome
	- stay on the page that give you Result
	- start dev tools console
	- change the manufactureur bellow (MANUFACTURER_FILTER = ...)
	- copy, paste and execute the whole script in the console
	- if the result look's like what you are looking for , copy/paste the commented delete loop to the console without comments
	- have fun
	
*/

// 1. Filter configuration
const MANUFACTURER_FILTER = "Home Assistant";

// 2. Data extraction
devices = await (await fetch('/nexus/v1/graphql', {
  method: 'POST',
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({
    query: `{ 
      endpoints { 
        items { 
          friendlyName
          legacyAppliance { 
            applianceId
            manufacturerName
            applianceTypes
            connectedVia
          }
          features { 
            name 
            properties { 
              ... on Reachability { reachabilityStatusValue } 
            } 
          } 
        } 
      } 
    }`
  })
})).json();

// 3. Filtering : manufacturerName + SCENE_TRIGGER ou ACTIVITY_TRIGGER
scenesToProcess = devices.data.endpoints.items
  .filter(device => {
    const matchesManu = device.legacyAppliance?.manufacturerName === MANUFACTURER_FILTER;
    const matchesType = device.legacyAppliance?.applianceTypes?.some(t =>
      t === "SCENE_TRIGGER" || t === "ACTIVITY_TRIGGER"
    );
    return matchesManu && matchesType;
  })
  .sort((a, b) => a.friendlyName.localeCompare(b.friendlyName))
  .map(device => ({
    friendlyName: device.friendlyName,
    applianceId: device.legacyAppliance.applianceId,
    type: device.legacyAppliance.applianceTypes[0],
    manufacturerName: device.legacyAppliance.manufacturerName
  }));

// 4. Result preview — To validate filtering before delete process
console.log(`Found ${scenesToProcess.length} scene(s)/script(s) to process out of ${devices.data.endpoints.items.length} total.`);
scenesToProcess.forEach(d => console.log(` - [${d.type}] ${d.friendlyName}`));
console.log("scenesToProcess:", scenesToProcess);

// 5. Deletion batch — (commented to avoid delete process before a verification of the scan :)  )
/*
for (const scene of scenesToProcess) {
  console.log("Deleting:", scene.friendlyName, `(${scene.type})`, scene.applianceId);
  let result = await fetch(`/api/phoenix/appliance/${encodeURIComponent(scene.applianceId)}`, {
    method: "DELETE",
    headers: {
      "Accept": "application/json",
      "Content-Type": "application/json"
    }
  });
  console.log("Result:", result.status, result.statusText);
}
*/

J’ai perdu une nuits la première fois et je me suis promis de ne pas le refaire … alors j’espère que ça vous aider un peu en cas de galère.

Bonne continuation

2 « J'aime »