feat: Refactor automation and site document screens, add device management features, and implement Home Assistant API integration

This commit is contained in:
2026-01-30 12:06:25 +01:00
parent 44d021891f
commit 9bb8279631
7 changed files with 543 additions and 127 deletions

142
utils/haApi.ts Normal file
View File

@ -0,0 +1,142 @@
import axios, { AxiosError } from 'axios';
import { HaArea, HaEntity } from '@/types/types';
// CONFIGURAZIONE
const HA_API_URL = process.env.EXPO_PUBLIC_HA_API_URL;
const HA_TOKEN = process.env.EXPO_PUBLIC_HA_TOKEN;
// Crea un'istanza di axios per Home Assistant
const haApi = axios.create({
baseURL: HA_API_URL,
headers: {
'Authorization': `Bearer ${HA_TOKEN}`,
'Content-Type': 'application/json',
},
timeout: 5000, // 5 secondi di timeout
});
haApi.interceptors.request.use((config) => {
console.log(`[HOME ASSISTANT API REQUEST] ${config.method?.toUpperCase()} ${config.url}`);
return config;
});
/*
* Connection test
*/
export const testHaConnection = async (): Promise<{ success: boolean; message: string }> => {
// Controlla se le variabili d'ambiente sono caricate
if (!HA_API_URL || !HA_TOKEN) {
console.error("Variabili d'ambiente per Home Assistant non trovate. Assicurati che EXPO_PUBLIC_HA_API_URL and EXPO_PUBLIC_HA_TOKEN siano definite nel file .env");
return { success: false, message: "Configurazione API per Home Assistant mancante." };
}
try {
const response = await haApi.get('/');
// Se la risposta è OK, HA restituisce un JSON con 'message'
if (response.status === 200 && response.data.message) {
return { success: true, message: response.data.message };
}
return { success: false, message: "Risposta inattesa dal server Home Assistant." };
} catch (error) {
const axiosError = error as AxiosError;
if (axiosError.code === 'ECONNABORTED' || axiosError.message.includes('timeout')) {
return { success: false, message: "Timeout: Il server non risponde (IP errato o server offline)." };
}
if (axiosError.response) {
// Errori con una risposta dal server (es. 401, 404)
if (axiosError.response.status === 401) {
return { success: false, message: "Errore 401: Token non autorizzato. Controlla il Long-Lived Token." };
}
if (axiosError.response.status === 404) {
return { success: false, message: "Errore 404: Verifica la configurazione di Home Assistant." };
}
return { success: false, message: `Errore server: Status ${axiosError.response.status}` };
} else if (axiosError.request) {
// Errori di rete (la richiesta è partita ma non ha ricevuto risposta)
return { success: false, message: `Errore di rete: Impossibile raggiungere ${HA_API_URL}` };
} else {
// Errore generico
return { success: false, message: `Errore sconosciuto: ${axiosError.message}` };
}
}
};
// Fetch Home Assistant Areas
export const getHaAreas = async (): Promise<HaArea[]> => {
try {
const response = await haApi.post('/template', {
"template": `
[
{%- for area_id in areas() %}
{%- set area_name = area_name(area_id) %}
{%- set f_id = floor_id(area_name) %}
{%- set f_name = floor_name(f_id) %}
{
"id": "{{ area_id }}",
"name": "{{ area_name }}",
"floor_id": {% if f_id != None %}"{{ f_id }}"{% else %}null{% endif %},
"floor_name": {% if f_name != None %}"{{ f_name }}"{% else %}null{% endif %},
"device_count": {{area_devices(area_id)|count}}
}{% if not loop.last %},{% endif %}
{%- endfor %}
]
`
});
const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;
// console.log("Aree recuperate da Home Assistant:", data);
return data;
} catch (error) {
console.error("Errore recupero aree:", error);
return []; // Restituisce un array vuoto in caso di errore
}
};
// Fetch Home Assistant Entities by Area
export const getHaEntitiesByArea = async (areaId: string): Promise<HaEntity[]> => {
try {
const response = await haApi.post('/template', {
"template": `
[
{%- set area_entities = area_entities('${areaId}') %}
{%- for entity_id in area_entities %}
{
"entity_id": {{ entity_id | tojson }},
"name": {{ state_attr(entity_id, 'friendly_name') | tojson }},
"state": {{ states(entity_id) | tojson }}
}{% if not loop.last %},{% endif %}
{%- endfor %}
]
`
});
const data = typeof response.data === 'string' ? JSON.parse(response.data) : response.data;
// console.log(`Entità recuperate per l'area ${areaId}:`, data);
return data;
} catch (error) {
console.error(`Errore recupero entità per l'area ${areaId}:`, error);
return []; // Restituisce un array vuoto in caso di errore
}
};
// Toggle Home Assistant Entity
export const toggleHaEntity = async (entityId: string): Promise<boolean> => {
try {
const domain = entityId.split('.')[0];
await haApi.post('/services/' + domain + '/toggle', {
entity_id: entityId
});
return true;
} catch (error) {
console.error(`Errore toggle ${entityId}:`, error);
return false;
}
};