├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feedback.yaml │ ├── pull_request_template.md │ └── bug_report.yml └── workflows │ ├── hacs.yaml │ ├── inchide-issue-uri-incomplete.yaml.bk │ └── release.yaml ├── hacs.json ├── custom_components └── infpro │ ├── manifest.json │ ├── translations │ ├── en.json │ ├── ro.json │ ├── es.json │ ├── fr.json │ └── de.json │ ├── api.py │ ├── coordinator.py │ ├── __init__.py │ ├── config_flow.py │ ├── const.py │ └── sensor.py ├── LICENSE ├── THANKS.md ├── FAQ.md ├── DEBUG.md └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | buy_me_a_coffee: cnecrea 3 | -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cutremur România (INFP)", 3 | "render_readme": true, 4 | "country": ["RO"] 5 | } 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ghid și întrebări frecvente 4 | url: https://github.com/cnecrea/infpro/blob/main/FAQ.md 5 | about: Documentație și întrebări frecvente” oferă informații detaliate despre utilizarea, configurarea și funcționalitățile integrării, precum și răspunsuri la cele mai comune întrebări ale utilizatorilor. 6 | -------------------------------------------------------------------------------- /custom_components/infpro/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain": "infpro", 3 | "name": "Cutremur România (INFP)", 4 | "codeowners": [ 5 | "@cnecrea" 6 | ], 7 | "config_flow": true, 8 | "dependencies": [], 9 | "documentation": "https://github.com/cnecrea/infpro", 10 | "iot_class": "cloud_polling", 11 | "issue_tracker": "https://github.com/cnecrea/infpro/issues", 12 | "requirements": [], 13 | "version": "2.1.1" 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/hacs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: HACS Combined Jobs 3 | 4 | on: 5 | push: 6 | pull_request: 7 | release: 8 | types: 9 | - published 10 | schedule: 11 | - cron: "0 0 * * *" 12 | 13 | permissions: 14 | contents: read 15 | packages: read 16 | statuses: write 17 | 18 | jobs: 19 | validate: 20 | name: Hassfest Validation 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | - name: Run Hassfest 26 | uses: home-assistant/actions/hassfest@master 27 | 28 | hacs: 29 | name: HACS Validation 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v4 34 | - name: Run HACS Action 35 | uses: hacs/action@main 36 | with: 37 | category: "integration" 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licență MIT 2 | 3 | Copyright (c) 2024 [cnecrea] 4 | 5 | Prin prezenta se acordă permisiunea, gratuit, oricărei persoane care intră în posesia 6 | unei copii a acestui software și a fișierelor de documentație asociate (denumite în 7 | continuare „Software”), să utilizeze Software-ul fără restricții, inclusiv, dar fără a 8 | se limita la drepturile de a folosi, copia, modifica, fuziona, publica, distribui, 9 | sublicenția și/sau vinde copii ale Software-ului, și de a permite persoanelor cărora le 10 | este furnizat Software-ul să facă acest lucru, sub rezerva următoarelor condiții: 11 | 12 | Notificarea de copyright de mai sus și această notificare de permisiune vor fi incluse 13 | în toate copiile sau porțiunile substanțiale ale Software-ului. 14 | 15 | SOFTWARE-UL ESTE FURNIZAT „CA ATARE”, FĂRĂ NICIUN FEL DE GARANȚIE, EXPRESĂ SAU 16 | IMPLICITĂ, INCLUSIV, DAR FĂRĂ A SE LIMITA LA GARANȚIILE DE COMERCIALIZARE, 17 | POTRIVIRE PENTRU UN ANUMIT SCOP ȘI NEÎNCĂLCARE. ÎN NICIUN CAZ AUTORII SAU 18 | DEȚINĂTORII DREPTURILOR DE AUTOR NU VOR FI RĂSPUNZĂTORI PENTRU NICIUN FEL DE 19 | PRETENȚII, DAUNE SAU ALTE RĂSPUNDERI, FIE ÎNTR-O ACȚIUNE CONTRACTUALĂ, DELICTUALĂ 20 | SAU ÎN ALTE CAZURI, CARE DECURG DIN, DIN SAU ÎN LEGĂTURĂ CU SOFTWARE-UL SAU 21 | UTILIZAREA SAU ALTE INTERACȚIUNI CU SOFTWARE-UL. 22 | -------------------------------------------------------------------------------- /THANKS.md: -------------------------------------------------------------------------------- 1 | # Mulțumiri speciale! 🙏 2 | 3 | Vreau să le mulțumesc din suflet tuturor celor care m-au susținut și au contribuit la dezvoltarea acestui proiect. Fiecare donație contează enorm și îmi oferă motivația de a continua să îmbunătățesc această integrare! 💛 4 | 5 | ## Donații recente 💛 6 | 7 | Le sunt recunoscător următorilor susținători pentru generozitatea lor: 8 | 9 | - **Birau Dorin** 10 | - **JaR** 11 | - **Șerban B** 12 | - **Robert Olteanu** 13 | - **Tudor H (HaT)** 14 | - **Andrei Ion** 15 | - **Gabi Dragoi** 16 | - **bored_mthfkr** 17 | - **Sebastian** 18 | - **Adrian Crișan** 19 | - **Lazar Dan Cristian** 20 | - **Horațiu Slăvescu** 21 | - **Attila** 22 | - **Rusu Radu** 23 | - **Șerbănescu Bogdan** 24 | - **Gabriel Sfâca** 25 | - **ovydyu1985** 26 | - **Dorin** 27 | - **Abulafiab - Bogdan** 28 | - **Mari** 29 | - **Marius Demian** 30 | - **Andrei Bădescu** 31 | - **GDaniel** 32 | - **Tavi** 33 | - **Cosmin Grigoraș** 34 | 35 | --- 36 | 37 | ## Cum poți contribui? 38 | Dacă vrei să mă susții și să te alături acestei liste, poți să faci o donație prin [BuyMeACoffee](https://www.buymeacoffee.com/cnecrea). Fiecare contribuție mă ajută să continui să dezvolt acest proiect și este apreciată din toată inima! 🙏 39 | 40 | Mulțumesc din suflet tuturor celor care au fost alături de mine! 💛 41 | -------------------------------------------------------------------------------- /custom_components/infpro/translations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Romania Earthquake (INFP)", 3 | "config": { 4 | "step": { 5 | "user": { 6 | "title": "Configure Romania Earthquake (INFP)", 7 | "description": "Enter the update interval (in seconds) and select a city.", 8 | "data": { 9 | "update_interval": "Update interval (in seconds)", 10 | "oras_id": "Select the city to monitor" 11 | } 12 | } 13 | }, 14 | "error": { 15 | "invalid_update_interval": "Invalid interval. It must be between 10 and 3600.", 16 | "oras_invalid": "Invalid city. Please select a city from the list." 17 | }, 18 | "abort": { 19 | "already_configured": "The integration is already configured." 20 | } 21 | }, 22 | "options": { 23 | "step": { 24 | "init": { 25 | "title": "Additional settings for Romania Earthquake (INFP)", 26 | "description": "Modify the desired options, including the update interval and the city.", 27 | "data": { 28 | "update_interval": "Update interval (in seconds)", 29 | "oras_id": "Change the monitored city" 30 | } 31 | } 32 | }, 33 | "error": { 34 | "invalid_update_interval": "Invalid interval. It must be between 10 and 3600.", 35 | "oras_invalid": "Invalid city. Please select a city from the list." 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/infpro/translations/ro.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Cutremur România (INFP)", 3 | "config": { 4 | "step": { 5 | "user": { 6 | "title": "Configurați integrarea Cutremur România (INFP)", 7 | "description": "Introduceți intervalul de actualizare (în secunde) și selectați un oraș.", 8 | "data": { 9 | "update_interval": "Interval de actualizare (în secunde)", 10 | "oras_id": "Selectați orașul de monitorizare" 11 | } 12 | } 13 | }, 14 | "error": { 15 | "invalid_update_interval": "Interval invalid. Trebuie să fie între 10 și 3600.", 16 | "oras_invalid": "Oraș invalid. Vă rugăm să selectați un oraș din listă." 17 | }, 18 | "abort": { 19 | "already_configured": "Integrarea este deja configurată." 20 | } 21 | }, 22 | "options": { 23 | "step": { 24 | "init": { 25 | "title": "Setări suplimentare pentru Cutremur România (INFP)", 26 | "description": "Modifică opțiunile dorite, inclusiv intervalul de actualizare și orașul.", 27 | "data": { 28 | "update_interval": "Interval de actualizare (în secunde)", 29 | "oras_id": "Selectați orașul de monitorizare" 30 | } 31 | } 32 | }, 33 | "error": { 34 | "invalid_update_interval": "Interval invalid. Trebuie să fie între 10 și 3600.", 35 | "oras_invalid": "Oraș invalid. Vă rugăm să selectați un oraș din listă." 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/infpro/api.py: -------------------------------------------------------------------------------- 1 | """API pentru integrarea INFP.""" 2 | import aiohttp 3 | import async_timeout 4 | import logging 5 | 6 | from .const import URL_CUTREMUR 7 | 8 | _LOGGER = logging.getLogger(__name__) 9 | 10 | 11 | async def fetch_data(): 12 | """ 13 | Obține datele de la API-ul INFP. 14 | 15 | :return: Datele primite de la API sub formă de dicționar. 16 | """ 17 | _LOGGER.debug("Inițializare proces de obținere a datelor de la API-ul INFP.") 18 | try: 19 | async with aiohttp.ClientSession() as session: 20 | # Setăm un timeout pentru cererea HTTP 21 | async with async_timeout.timeout(10): # Timeout de 10 secunde 22 | _LOGGER.debug("Solicităm date de la URL: %s", URL_CUTREMUR) 23 | 24 | async with session.get(URL_CUTREMUR) as response: 25 | _LOGGER.debug("Răspuns primit cu status: %s", response.status) 26 | 27 | if response.status != 200: 28 | raise ValueError( 29 | f"HTTP error {response.status}: {response.reason}" 30 | ) 31 | 32 | data = await response.json() 33 | #_LOGGER.debug("Date obținute de la API: %s", data) 34 | 35 | return data 36 | 37 | except Exception as e: 38 | _LOGGER.error("Eroare la obținerea datelor de la API-ul INFP: %s", e) 39 | raise 40 | 41 | -------------------------------------------------------------------------------- /custom_components/infpro/translations/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Terremotos en Rumanía (INFP)", 3 | "config": { 4 | "step": { 5 | "user": { 6 | "title": "Configurar Terremotos en Rumanía (INFP)", 7 | "description": "Introduzca el intervalo de actualización (en segundos) y seleccione una ciudad.", 8 | "data": { 9 | "update_interval": "Intervalo de actualización (en segundos)", 10 | "oras_id": "Seleccione la ciudad a monitorear" 11 | } 12 | } 13 | }, 14 | "error": { 15 | "invalid_update_interval": "Intervalo inválido. Debe estar entre 10 y 3600.", 16 | "oras_invalid": "Ciudad inválida. Por favor, seleccione una ciudad de la lista." 17 | }, 18 | "abort": { 19 | "already_configured": "La integración ya está configurada." 20 | } 21 | }, 22 | "options": { 23 | "step": { 24 | "init": { 25 | "title": "Configuraciones adicionales para Terremotos en Rumanía (INFP)", 26 | "description": "Modifique las opciones deseadas, incluido el intervalo de actualización y la ciudad.", 27 | "data": { 28 | "update_interval": "Intervalo de actualización (en segundos)", 29 | "oras_id": "Cambiar la ciudad monitoreada" 30 | } 31 | } 32 | }, 33 | "error": { 34 | "invalid_update_interval": "Intervalo inválido. Debe estar entre 10 y 3600.", 35 | "oras_invalid": "Ciudad inválida. Por favor, seleccione una ciudad de la lista." 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/infpro/translations/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Séismes en Roumanie (INFP)", 3 | "config": { 4 | "step": { 5 | "user": { 6 | "title": "Configurer Séismes en Roumanie (INFP)", 7 | "description": "Entrez l'intervalle de mise à jour (en secondes) et sélectionnez une ville.", 8 | "data": { 9 | "update_interval": "Intervalle de mise à jour (en secondes)", 10 | "oras_id": "Sélectionnez la ville à surveiller" 11 | } 12 | } 13 | }, 14 | "error": { 15 | "invalid_update_interval": "Intervalle invalide. Il doit être compris entre 10 et 3600.", 16 | "oras_invalid": "Ville invalide. Veuillez sélectionner une ville dans la liste." 17 | }, 18 | "abort": { 19 | "already_configured": "L'intégration est déjà configurée." 20 | } 21 | }, 22 | "options": { 23 | "step": { 24 | "init": { 25 | "title": "Paramètres supplémentaires pour Séismes en Roumanie (INFP)", 26 | "description": "Modifiez les options souhaitées, y compris l'intervalle de mise à jour et la ville.", 27 | "data": { 28 | "update_interval": "Intervalle de mise à jour (en secondes)", 29 | "oras_id": "Changer la ville surveillée" 30 | } 31 | } 32 | }, 33 | "error": { 34 | "invalid_update_interval": "Intervalle invalide. Il doit être compris entre 10 et 3600.", 35 | "oras_invalid": "Ville invalide. Veuillez sélectionner une ville dans la liste." 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/infpro/translations/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Erdbeben in Rumänien (INFP)", 3 | "config": { 4 | "step": { 5 | "user": { 6 | "title": "Erdbeben in Rumänien (INFP) konfigurieren", 7 | "description": "Geben Sie das Aktualisierungsintervall (in Sekunden) ein und wählen Sie eine Stadt aus.", 8 | "data": { 9 | "update_interval": "Aktualisierungsintervall (in Sekunden)", 10 | "oras_id": "Wählen Sie die zu überwachende Stadt aus" 11 | } 12 | } 13 | }, 14 | "error": { 15 | "invalid_update_interval": "Ungültiges Intervall. Es muss zwischen 10 und 3600 liegen.", 16 | "oras_invalid": "Ungültige Stadt. Bitte wählen Sie eine Stadt aus der Liste aus." 17 | }, 18 | "abort": { 19 | "already_configured": "Die Integration ist bereits konfiguriert." 20 | } 21 | }, 22 | "options": { 23 | "step": { 24 | "init": { 25 | "title": "Zusätzliche Einstellungen für Erdbeben in Rumänien (INFP)", 26 | "description": "Passen Sie die gewünschten Optionen an, einschließlich des Aktualisierungsintervalls und der Stadt.", 27 | "data": { 28 | "update_interval": "Aktualisierungsintervall (in Sekunden)", 29 | "oras_id": "Ändern Sie die überwachte Stadt" 30 | } 31 | } 32 | }, 33 | "error": { 34 | "invalid_update_interval": "Ungültiges Intervall. Es muss zwischen 10 und 3600 liegen.", 35 | "oras_invalid": "Ungültige Stadt. Bitte wählen Sie eine Stadt aus der Liste aus." 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /custom_components/infpro/coordinator.py: -------------------------------------------------------------------------------- 1 | """Coordonator pentru integrarea INFP.""" 2 | import asyncio 3 | from datetime import timedelta 4 | import logging 5 | 6 | from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed 7 | 8 | from .api import fetch_data 9 | from .const import DOMAIN 10 | 11 | _LOGGER = logging.getLogger(__name__) 12 | 13 | 14 | class InfProDataUpdateCoordinator(DataUpdateCoordinator): 15 | """Coordonator pentru gestionarea actualizărilor de date.""" 16 | 17 | def __init__(self, hass, update_interval): 18 | """Inițializează coordonatorul.""" 19 | super().__init__( 20 | hass, 21 | _LOGGER, 22 | name=f"{DOMAIN}_data_coordinator", 23 | update_interval=timedelta(seconds=update_interval), 24 | ) 25 | _LOGGER.debug( 26 | "INFPDataUpdateCoordinator inițializat cu un interval de actualizare de %s secunde.", 27 | update_interval, 28 | ) 29 | 30 | async def _async_update_data(self): 31 | """Actualizează datele prin API.""" 32 | _LOGGER.debug("Inițiere proces de actualizare a datelor prin API.") 33 | try: 34 | # Apelează API-ul pentru a obține date actualizate 35 | data = await fetch_data() 36 | #_LOGGER.debug("Date actualizate cu succes: %s", data) 37 | return data 38 | except Exception as err: 39 | _LOGGER.error( 40 | "Eroare la actualizarea datelor prin API: %s", err, exc_info=True 41 | ) 42 | raise UpdateFailed(f"Eroare la actualizarea datelor: {err}") 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feedback.yaml: -------------------------------------------------------------------------------- 1 | name: Feedback 2 | description: Oferiți feedback despre integrarea Cutremur România (INFP) sau sugerați îmbunătățiri. 3 | title: "Titlul propunerii sau al observației dvs." 4 | labels: 5 | - îmbunătățire 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Mulțumim că v-ați luat timp să ne oferiți feedback! 🙌 11 | Completați informațiile de mai jos pentru a ne ajuta să înțelegem mai bine sugestiile sau observațiile dvs. 12 | 13 | - type: textarea 14 | id: descriere 15 | attributes: 16 | label: Descrieți feedback-ul sau sugestia 17 | description: Furnizați detalii clare despre observațiile sau îmbunătățirile pe care le sugerați. 18 | placeholder: "Scrieți aici sugestia sau observația dvs..." 19 | validations: 20 | required: true 21 | 22 | - type: input 23 | id: beneficiu 24 | attributes: 25 | label: Care este beneficiul acestei îmbunătățiri? 26 | description: Cum credeți că această schimbare ar putea ajuta utilizatorii? 27 | placeholder: "Descrieți impactul pozitiv..." 28 | validations: 29 | required: true 30 | 31 | - type: dropdown 32 | id: prioritate 33 | attributes: 34 | label: Ce nivel de prioritate considerați că are această sugestie? 35 | description: Selectați nivelul de importanță. 36 | options: 37 | - Scăzut 38 | - Mediu 39 | - Ridicat 40 | validations: 41 | required: true 42 | 43 | - type: textarea 44 | id: informatii_suplimentare 45 | attributes: 46 | label: Alte informații relevante 47 | description: Adăugați orice alte detalii pe care le considerați utile. 48 | placeholder: "Exemple, contexte sau alte detalii..." 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Schimbări propuse 2 | 7 | 8 | ## Descriere 9 | 13 | 14 | Exemplu: Acest PR rezolvă problema X prin implementarea Y. 15 | 16 | --- 17 | 18 | ## Tipul modificării 19 | 23 | 24 | - [ ] Remediere de bug (fix pentru o problemă existentă) 25 | - [ ] Funcționalitate nouă (introduce o caracteristică fără să afecteze altele) 26 | - [ ] Schimbare majoră (modifică funcționalități existente sau introduce incompatibilități) 27 | - [ ] Îmbunătățire (optimizare de cod, refactorizare, etc.) 28 | - [ ] Documentație (actualizare sau adăugare documentație) 29 | 30 | --- 31 | 32 | ## Informații suplimentare 33 | 37 | 38 | - Acest PR rezolvă problema: Closes # 39 | - Link către baza de cod modificată: 40 | - Link către documentația asociată: 41 | 42 | --- 43 | 44 | ## Checklist 45 | 49 | 50 | - [ ] Codul a fost testat local și funcționează conform așteptărilor. 51 | - [ ] Am actualizat documentația relevantă, dacă este necesar. 52 | - [ ] Modificările respectă stilul de cod al proiectului. 53 | - [ ] PR-ul este gata pentru revizuire. 54 | 55 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Întrebări frecvente 5 | - [Cum să adaug integrarea în Home Assistant?](#cum-să-adaug-integrarea-în-home-assistant) 6 | 7 | 8 | ## Cum să adaug integrarea în Home Assistant? 9 | 10 | Pentru a reveni la începutul paginii, [apăsați aici](#top). 11 | 12 | 13 | **Răspuns:** 14 | HACS (Home Assistant Community Store) permite instalarea și gestionarea integrărilor, temelor și modulelor personalizate create de comunitate. Urmează pașii de mai jos pentru a adăuga un repository extern în HACS și pentru a instala o integrare: 15 | 16 | - **1. Asigură-te că HACS este instalat** 17 | - Verifică dacă HACS este deja instalat în Home Assistant. 18 | - Navighează la **Setări** > **Dispozitive și servicii** > **Integrări** și caută "HACS". 19 | - Dacă nu este instalat, urmează ghidul oficial de instalare pentru HACS: [HACS Installation Guide](https://hacs.xyz/docs/use). 20 | 21 | - **2. Găsește repository-ul extern** 22 | - Accesează pagina GitHub a integrării pe care vrei să o adaugi. De exemplu, repository-ul ar putea arăta astfel: 23 | `https://github.com/autorul-integarii/nume-integrare`. 24 | 25 | - **3. Adaugă repository-ul în HACS** 26 | - În Home Assistant, mergi la **HACS** din bara laterală. 27 | - Apasă pe butonul cu **cele trei puncte** din colțul din dreapta sus și selectează **Repositories**. 28 | - În secțiunea "Custom repositories", introdu URL-ul repository-ului extern (de exemplu, `https://github.com/autorul-integarii/nume-integrare`). 29 | - Selectează tipul de repository: 30 | - **Integration** pentru integrări. 31 | - **Plugin** pentru module front-end. 32 | - **Theme** pentru teme. 33 | - Apasă pe **Add** pentru a adăuga repository-ul. 34 | 35 | - **4. Instalează integrarea** 36 | - După ce repository-ul a fost adăugat, mergi la **HACS** > **Integrations**. 37 | - Caută numele integrării pe care tocmai ai adăugat-o. 38 | - Apasă pe integrare și selectează **Download** sau **Install**. 39 | - După instalare, Home Assistant îți poate solicita să repornești sistemul. Urmează instrucțiunile pentru a finaliza configurarea. 40 | 41 | - **5. Configurează integrarea** 42 | - După repornire, mergi la **Setări** > **Dispozitive și servicii** > **Adaugă integrare**. 43 | - Caută numele integrării instalate și urmează pașii de configurare specifici. 44 | 45 | > **Notă:** 46 | > Asigură-te că Home Assistant și HACS sunt actualizate la cea mai recentă versiune pentru a evita erorile de compatibilitate. 47 | 48 | -------------------------------------------------------------------------------- /custom_components/infpro/__init__.py: -------------------------------------------------------------------------------- 1 | """Integrarea INFP pentru Home Assistant.""" 2 | import logging 3 | 4 | from homeassistant.config_entries import ConfigEntry 5 | from homeassistant.core import HomeAssistant 6 | from homeassistant.helpers import config_validation as cv 7 | from .const import DOMAIN, PLATFORMS 8 | from .coordinator import InfProDataUpdateCoordinator 9 | 10 | CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) 11 | 12 | _LOGGER = logging.getLogger(__name__) 13 | 14 | 15 | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: 16 | """Configurează integrarea folosind un config entry.""" 17 | _LOGGER.debug("Inițiere configurare pentru integrarea INFP.") 18 | 19 | # Inițializare stocare date pentru domeniu 20 | hass.data.setdefault(DOMAIN, {}) 21 | 22 | # Preluare interval de actualizare 23 | update_interval = entry.options.get("UPDATE_INTERVAL", 30) 24 | _LOGGER.debug( 25 | "Intervalul de actualizare setat pentru coordonator: %s secunde.", update_interval 26 | ) 27 | 28 | # Creare coordonator 29 | _LOGGER.debug("Inițializare coordonator pentru integrarea INFP.") 30 | coordinator = InfProDataUpdateCoordinator( 31 | hass, update_interval=update_interval 32 | ) 33 | 34 | # Prima actualizare a datelor 35 | try: 36 | await coordinator.async_config_entry_first_refresh() 37 | _LOGGER.debug("Prima actualizare a datelor realizată cu succes.") 38 | except Exception as err: 39 | _LOGGER.error("Eroare la prima actualizare a datelor: %s", err) 40 | return False 41 | 42 | # Salvare coordonator în stocarea domeniului 43 | hass.data[DOMAIN][entry.entry_id] = { 44 | "coordinator": coordinator, 45 | } 46 | 47 | # Încărcare platforme asociate 48 | _LOGGER.debug("Încărcare platforme: %s.", PLATFORMS) 49 | await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) 50 | _LOGGER.debug("Integrarea INFP a fost configurată cu succes.") 51 | return True 52 | 53 | 54 | async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: 55 | """Elimină o configurație.""" 56 | _LOGGER.debug("Inițiere proces de dezinstalare pentru integrarea INFP.") 57 | 58 | # Dezinstalare platforme asociate 59 | unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) 60 | if unload_ok: 61 | # Eliminare coordonator din stocare 62 | _LOGGER.debug( 63 | "Coordonatorul a fost eliminat din stocare pentru intrarea cu ID-ul: %s.", 64 | entry.entry_id, 65 | ) 66 | 67 | _LOGGER.debug("Integrarea INFP a fost dezinstalată cu succes.") 68 | return unload_ok 69 | -------------------------------------------------------------------------------- /.github/workflows/inchide-issue-uri-incomplete.yaml.bk: -------------------------------------------------------------------------------- 1 | name: Închidere automată a issue-urilor incomplete 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | reason: 7 | description: "Motivul pentru care se închid issue-urile (ex. incomplete)" 8 | required: true 9 | default: "incomplete" 10 | type: string 11 | 12 | issues: 13 | types: 14 | - opened 15 | 16 | permissions: 17 | issues: write 18 | 19 | jobs: 20 | inchidere_si_blocare_issue: 21 | name: Închidere automată și blocare 22 | runs-on: ubuntu-22.04 23 | 24 | steps: 25 | # 1. Verifică corpul issue-ului pentru câmpuri incomplete 26 | - name: Verifică corpul issue-ului 27 | id: verifica 28 | run: | 29 | issue_body=$(echo "${{ github.event.issue.body }}" | sed 's/[^a-zA-Z0-9 .,!?()-]//g') 30 | 31 | if echo "$issue_body" | grep -qE "(None|Nu, nu am citit secțiunea FAQ.md|No response)"; then 32 | echo "close=true" >> $GITHUB_ENV 33 | else 34 | echo "close=false" >> $GITHUB_ENV 35 | fi 36 | 37 | # 2. Închide issue-ul dacă este incomplet 38 | - name: Închide issue-ul 39 | if: env.close == 'true' 40 | run: | 41 | curl -X PATCH \ 42 | -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ 43 | -H "Accept: application/vnd.github.v3+json" \ 44 | https://api.github.com/repos/cnecrea/infpro/issues/${{ github.event.issue.number }} \ 45 | -d '{"state": "closed"}' 46 | 47 | curl -X POST \ 48 | -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ 49 | -H "Accept: application/vnd.github.v3+json" \ 50 | https://api.github.com/repos/cnecrea/infpro/issues/${{ github.event.issue.number }}/comments \ 51 | -d '{"body": "Acest issue a fost închis automat deoarece nu au fost completate toate câmpurile obligatorii.\nTe rog să completezi informațiile necesare, inclusiv versiunea integrării, și să confirmi că ai citit [FAQ.md](https://github.com/cnecrea/infpro/blob/main/FAQ.md).\nDacă problema persistă, te rugăm să creezi un nou issue."}' 52 | 53 | # 3. Blochează issue-ul pentru a preveni comentarii suplimentare 54 | - name: Blochează issue-ul 55 | if: env.close == 'true' 56 | run: | 57 | curl -X PUT \ 58 | -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ 59 | -H "Accept: application/vnd.github.v3+json" \ 60 | https://api.github.com/repos/cnecrea/infpro/issues/${{ github.event.issue.number }}/lock \ 61 | -d '{"lock_reason": "resolved"}' 62 | 63 | # 4. Mesaj în log 64 | - name: Log notificare 65 | if: env.close == 'true' 66 | run: echo "Issue-ul a fost închis și blocat automat de GitHub Actions." 67 | -------------------------------------------------------------------------------- /DEBUG.md: -------------------------------------------------------------------------------- 1 | 2 | # Debugging pentru integrarea personalizată 3 | 4 | Acest ghid oferă pașii necesari pentru a activa logarea detaliată și pentru a analiza problemele dintr-o integrare personalizată. 5 | 6 | --- 7 | 8 | ## 1. Activează logarea detaliată 9 | 10 | ### Adaugă în `configuration.yaml`: 11 | Pentru a activa logarea detaliată pentru integrarea ta personalizată, editează fișierul `configuration.yaml` și adaugă următoarele: 12 | ```yaml 13 | logger: 14 | default: warning 15 | logs: 16 | custom_components.infpro: debug 17 | homeassistant.const: critical 18 | homeassistant.loader: critical 19 | homeassistant.helpers.frame: critical 20 | ``` 21 | 22 | ### Restartează sistemul 23 | După ce ai salvat fișierul, repornește sistemul Home Assistant pentru ca modificările să fie aplicate. 24 | 25 | --- 26 | 27 | ## 2. Analizează logurile 28 | 29 | ### Localizarea logurilor 30 | Logurile se află, de obicei, în fișierul `home-assistant.log`, în directorul principal al Home Assistant. 31 | 32 | ### Filtrarea logurilor 33 | Pentru a găsi rapid informațiile relevante despre integrarea ta, poți folosi comanda: 34 | ```bash 35 | grep 'custom_components.infpro' home-assistant.log 36 | ``` 37 | 38 | --- 39 | 40 | ## Notă 41 | Asigură-te că toate configurațiile din `configuration.yaml` sunt corecte înainte de a începe procesul de debugging. 42 | 43 | 44 | 45 | # Cum să postezi cod în discuții 46 | 47 | Pentru a posta cod în mod corect și lizibil, utilizează blocuri de cod delimitate de trei backticks (```) urmate de limbajul codului. De exemplu, pentru YAML, folosește: 48 | 49 |
50 | ```yaml
51 | 2025-01-20 15:35:12 INFO custom_components.example_integration: Initializing Example Integration.
52 | 2025-01-20 15:35:13 DEBUG custom_components.example_integration: Configuration loaded: {'username': 'test_user', 'update_interval': 30}
53 | 2025-01-20 15:35:14 INFO custom_components.example_integration.api: Attempting to authenticate user 'test_user'.
54 | 2025-01-20 15:35:15 ERROR custom_components.example_integration.api: Authentication failed. Invalid credentials provided.
55 | 2025-01-20 15:35:16 DEBUG custom_components.example_integration: Retrying authentication in 10 seconds.
56 | ```
57 |
58 |
59 | Rezultatul va arăta astfel:
60 |
61 | ```yaml
62 | 2025-01-20 15:35:12 INFO custom_components.example_integration: Initializing Example Integration.
63 | 2025-01-20 15:35:13 DEBUG custom_components.example_integration: Configuration loaded: {'username': 'test_user', 'update_interval': 30}
64 | 2025-01-20 15:35:14 INFO custom_components.example_integration.api: Attempting to authenticate user 'test_user'.
65 | 2025-01-20 15:35:15 ERROR custom_components.example_integration.api: Authentication failed. Invalid credentials provided.
66 | 2025-01-20 15:35:16 DEBUG custom_components.example_integration: Retrying authentication in 10 seconds.
67 | ```
68 |
69 | ## Pași pentru a posta cod:
70 | 1. Scrie ` ```yaml ` (trei backticks urmate de "yaml").
71 | 2. Adaugă codul tău pe liniile următoare.
72 | 3. Încheie cu alte trei backticks: ` ``` `.
73 |
74 | Astfel, codul va fi formatat corespunzător și ușor de citit de ceilalți utilizatori.
75 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Raportare problemă
2 | description: Raportați o problemă legată de integrarea Cutremur România (INFP).
3 | title: "Descriere scurtă a problemei"
4 | labels: ajutor necesar
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: >
9 | ⚠️ **ATENȚIE:** ⚠️
10 |
11 | Înainte de a raporta o problemă, te rog să verifici secțiunea [FAQ.md](https://github.com/cnecrea/infpro/blob/main/FAQ.md) pentru a vedea dacă problema este deja explicată sau are o soluție.
12 |
13 | Acest lucru mă ajută să evit raportările duplicate și să rezolv problemele mai eficient.
14 |
15 | Dacă problema persistă, completează informațiile de mai jos.
16 |
17 | - type: dropdown
18 | id: citit_faq
19 | attributes:
20 | label: Ați citit secțiunea FAQ.md?
21 | description: Te rog să confirmi dacă ai citit fișierul FAQ.md înainte de a raporta problema.
22 | options:
23 | - "Nu, nu am citit secțiunea FAQ.md."
24 | - "Da, am citit secțiunea FAQ.md și nu am găsit răspunsul la problema mea."
25 | validations:
26 | required: true
27 |
28 | - type: textarea
29 | id: descriere
30 | attributes:
31 | label: Descrieți problema
32 | description: |
33 | Explicați clar ce s-a întâmplat, inclusiv pașii pentru a reproduce problema.
34 | **Nu introduceți logurile aici.** Logurile trebuie raportate în secțiunea dedicată de mai jos.
35 | placeholder: "Descrieți aici problema (fără loguri)"
36 | validations:
37 | required: true
38 |
39 | - type: input
40 | id: versiune
41 | attributes:
42 | label: Versiunea integrării
43 | description: Specificați versiunea actuală instalată (o puteți verifica în `custom_components/infpro/manifest.json`).
44 | placeholder: "Exemplu: 2.0.0"
45 | validations:
46 | required: true
47 |
48 | - type: markdown
49 | attributes:
50 | value: >
51 |
52 |
53 | - type: markdown
54 | attributes:
55 | value: >
56 | ---
57 |
58 | - type: textarea
59 | id: loguri
60 | attributes:
61 | label: Loguri relevante
62 | description: |
63 | Adăugați logurile relevante pentru problemă. Utilizați formatul următor:
64 | ```yaml
65 | # Exemplu:
66 | 2025-01-14 03:08:37.818 ERROR (MainThread) [custom_components.infpro.sensor] Eroare la parsarea valorii: 22
67 | ```
68 |
69 | Dacă nu știți cum să activați modul debug și să colectați logurile relevante,
70 | vă rugăm să consultați [ghidul pentru debugging](https://github.com/cnecrea/infpro/blob/main/DEBUG.md).
71 |
72 | După ce ați activat modul debug, reporniți Home Assistant și reproduceți problema,
73 | apoi copiați logurile generate din fișierul `home-assistant.log`.
74 |
75 | render: yaml
76 | validations:
77 | required: true
78 |
79 | - type: dropdown
80 | id: restart
81 | attributes:
82 | label: Ați încercat să dați restart?
83 | description: Confirmă dacă ați încercat să dați restart la Home Assistant.
84 | options:
85 | - "Da"
86 | - "Nu"
87 | validations:
88 | required: true
89 |
90 | - type: input
91 | id: sistem_operare
92 | attributes:
93 | label: Sistem de operare
94 | description: Specificați sistemul de operare utilizat (ex. Home Assistant OS, Docker, etc.).
95 | placeholder: "Exemplu: Home Assistant OS"
96 |
--------------------------------------------------------------------------------
/custom_components/infpro/config_flow.py:
--------------------------------------------------------------------------------
1 | """Config flow pentru integrarea INFP."""
2 | import voluptuous as vol
3 | import logging
4 | from homeassistant import config_entries
5 | from homeassistant.core import callback
6 |
7 | from .const import DOMAIN, UPDATE_INTERVAL, DEFAULT_ORAS, LISTA_ORASE
8 |
9 | _LOGGER = logging.getLogger(__name__)
10 |
11 |
12 | class InfProConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
13 | """Flux de configurare pentru integrarea INFP."""
14 |
15 | VERSION = 1
16 |
17 | async def async_step_user(self, user_input=None):
18 | """Primul pas din configurare."""
19 | if user_input is not None:
20 | # Obține numele orașului bazat pe ID-ul selectat
21 | oras_id = user_input["oras_id"]
22 | orase = {oras.split(": ")[0]: oras.split(": ")[1] for oras in LISTA_ORASE}
23 | oras_nume = orase.get(oras_id, "Necunoscut")
24 |
25 | _LOGGER.debug(
26 | "Utilizatorul a selectat orașul: ID=%s, Nume oraș=%s",
27 | oras_id,
28 | oras_nume,
29 | )
30 |
31 | # Conversia cheii update_interval la litere mici
32 | user_input = {
33 | "update_interval": user_input["update_interval"],
34 | "oras_id": oras_id,
35 | "oras_nume": oras_nume,
36 | }
37 |
38 | return self.async_create_entry(
39 | title=self.hass.data.get("translations", {}).get("config.title", "Cutremur România (INFP)"),
40 | data=user_input,
41 | )
42 |
43 | _LOGGER.debug("Inițializare formular pentru configurarea INFP.")
44 |
45 | orase = {oras.split(": ")[0]: oras.split(": ")[1] for oras in LISTA_ORASE}
46 |
47 | schema = vol.Schema({
48 | vol.Required(
49 | "update_interval",
50 | default=UPDATE_INTERVAL
51 | ): vol.All(vol.Coerce(int), vol.Range(min=30)),
52 | vol.Required(
53 | "oras_id",
54 | default=DEFAULT_ORAS
55 | ): vol.In(orase),
56 | })
57 |
58 | return self.async_show_form(
59 | step_id="user",
60 | data_schema=schema,
61 | description_placeholders={
62 | "description": self.hass.data.get("translations", {}).get("config.step.user.description", "")
63 | }
64 | )
65 |
66 | @staticmethod
67 | @callback
68 | def async_get_options_flow(config_entry):
69 | """Returnează fluxul de opțiuni."""
70 | return InfProOptionsFlow(config_entry)
71 |
72 |
73 | class InfProOptionsFlow(config_entries.OptionsFlow):
74 | """Flux de opțiuni pentru integrarea INFP."""
75 |
76 | def __init__(self, config_entry: config_entries.ConfigEntry):
77 | """Inițializează fluxul de opțiuni."""
78 | super().__init__()
79 | self._config_entry = config_entry
80 |
81 | async def async_step_init(self, user_input=None):
82 | """Pasul inițial pentru fluxul de opțiuni."""
83 | if user_input is not None:
84 | # Actualizăm numele orașului la modificarea opțiunilor
85 | oras_id = user_input["oras_id"]
86 | orase = {oras.split(": ")[0]: oras.split(": ")[1] for oras in LISTA_ORASE}
87 | oras_nume = orase.get(oras_id, "Necunoscut")
88 |
89 | _LOGGER.debug(
90 | "Utilizatorul a actualizat orașul: ID=%s, Nume=%s",
91 | oras_id,
92 | oras_nume,
93 | )
94 |
95 | user_input["oras_nume"] = oras_nume
96 | return self.async_create_entry(title="", data=user_input)
97 |
98 | _LOGGER.debug("Inițializare formular pentru opțiunile fluxului INFP.")
99 |
100 | orase = {oras.split(": ")[0]: oras.split(": ")[1] for oras in LISTA_ORASE}
101 |
102 | schema = vol.Schema({
103 | vol.Required(
104 | "update_interval",
105 | default=self.config_entry.options.get("update_interval", UPDATE_INTERVAL)
106 | ): vol.All(vol.Coerce(int), vol.Range(min=30)),
107 | vol.Required(
108 | "oras_id",
109 | default=self.config_entry.options.get("oras_id", DEFAULT_ORAS)
110 | ): vol.In(orase),
111 | })
112 |
113 | return self.async_show_form(
114 | step_id="init",
115 | data_schema=schema,
116 | description_placeholders={
117 | "description": self.hass.data.get("translations", {}).get("config.step.init.description", "")
118 | }
119 | )
120 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Creează lansare de tip draft
2 |
3 | on:
4 | workflow_dispatch: # Permite rularea manuală a workflow-ului cu parametri
5 | inputs:
6 | increment_type:
7 | description: "Tipul de incrementare al versiunii (major/minor/patch)"
8 | required: true
9 | default: "patch" # Implicit se incrementează PATCH
10 | type: choice
11 | options:
12 | - major
13 | - minor
14 | - patch
15 |
16 | jobs:
17 | release:
18 | name: Creează Lansare de tip draft
19 | runs-on: ubuntu-22.04 # Ubuntu 22.04 este stabil și suportă Python 3.10
20 |
21 | steps:
22 | # 1. Checkout repository
23 | - name: Checkout repository
24 | uses: actions/checkout@v4
25 |
26 | # 2. Set up Python (versiune compatibilă)
27 | - name: Set up Python
28 | uses: actions/setup-python@v5
29 | with:
30 | python-version: '3.10' # Versiune stabilă și compatibilă
31 |
32 | # 3. Update manifest.json version
33 | - name: Update manifest.json version
34 | id: update_manifest
35 | run: |
36 | # Verifică dacă jq este instalat
37 | if ! command -v jq &> /dev/null; then
38 | echo "Installing jq..."
39 | sudo apt-get update && sudo apt-get install -y jq
40 | fi
41 |
42 | # Definim calea către manifest.json
43 | MANIFEST_PATH="custom_components/infpro/manifest.json"
44 |
45 | # Verificăm dacă fișierul există
46 | if [ ! -f "$MANIFEST_PATH" ]; then
47 | echo "Error: manifest.json not found at $MANIFEST_PATH"
48 | exit 1
49 | fi
50 |
51 | # Citește versiunea actuală
52 | VERSION=$(jq -r '.version' "$MANIFEST_PATH")
53 | echo "Current version: $VERSION"
54 |
55 | # Sparge versiunea în MAJOR, MINOR și PATCH
56 | IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
57 |
58 | # Verificăm tipul de incrementare
59 | INCREMENT_TYPE="${{ github.event.inputs.increment_type }}"
60 | echo "Increment type: $INCREMENT_TYPE"
61 |
62 | if [ "$INCREMENT_TYPE" = "major" ]; then
63 | MAJOR=$((MAJOR + 1))
64 | MINOR=0
65 | PATCH=0
66 | elif [ "$INCREMENT_TYPE" = "minor" ]; then
67 | MINOR=$((MINOR + 1))
68 | PATCH=0
69 | elif [ "$INCREMENT_TYPE" = "patch" ]; then
70 | PATCH=$((PATCH + 1))
71 | else
72 | echo "Invalid increment type: $INCREMENT_TYPE. Use 'major', 'minor', or 'patch'."
73 | exit 1
74 | fi
75 |
76 | # Creează noua versiune
77 | NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
78 | echo "New version: $NEW_VERSION"
79 |
80 | # Actualizează manifest.json
81 | jq --arg version "$NEW_VERSION" '.version = $version' "$MANIFEST_PATH" > manifest.tmp && mv manifest.tmp "$MANIFEST_PATH"
82 |
83 | # Setează noua versiune ca variabilă de mediu
84 | echo "new_version=$NEW_VERSION" >> $GITHUB_ENV
85 |
86 | # 4. Commit și push modificările folosind PAT
87 | - name: Commit changes and push
88 | env:
89 | PAT: ${{ secrets.PAT_TOKEN }} # Token-ul salvat ca secret
90 | run: |
91 | # Configurare GitHub Actions bot
92 | git config user.name "infpro-release-bot"
93 | git config user.email "infpro-bot@users.noreply.github.com"
94 |
95 | # Setează URL-ul remote pentru autentificare cu token
96 | git remote set-url origin https://x-access-token:${PAT}@github.com/cnecrea/infpro.git
97 |
98 | # Adaugă și face commit la modificări
99 | git add custom_components/infpro/manifest.json
100 | git commit -m "Update version to ${{ env.new_version }}"
101 |
102 | # Push la modificări
103 | git push origin HEAD
104 |
105 | # 5. Creează un fișier .zip doar cu conținutul folderului infpro
106 | - name: Create ZIP Archive
107 | run: |
108 | mkdir -p dist
109 | cd custom_components/infpro
110 | zip -r ../../dist/infpro.zip ./*
111 |
112 | # 6. Creează un release pe GitHub ca draft
113 | - name: Create GitHub Release
114 | uses: softprops/action-gh-release@v1
115 | with:
116 | tag_name: "${{ env.new_version }}" # Creează un tag
117 | name: "${{ env.new_version }}" # Nume pentru release
118 | body: |
119 | ## Lansare de tip draft
120 | - Această lansare a fost generată automat de GitHub Actions.
121 | - Verificați conținutul înainte de publicare.
122 | files: dist/infpro.zip # Include fișierul ZIP în release
123 | draft: true # Creează release-ul ca draft
124 | env:
125 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Token-ul implicit GitHub
126 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | # Cutremur România (INFP) - Integrare pentru Home Assistant 🏠🇷🇴
5 |
6 | Această integrare pentru Home Assistant oferă **doi senzori** care monitorizează datele seismice din România, folosind informațiile oficiale de la **Institutul Național pentru Fizica Pământului (INFP)**. Integrarea este configurabilă prin interfața UI și permite personalizarea orașului monitorizat și a intervalului de actualizare. 🚀
7 |
8 | ## 🌟 Caracteristici
9 |
10 | ### Senzor `Analiză date`:
11 | - **🔍 Monitorizare Impact Oraș**:
12 | - Afișează date detaliate despre impactul cutremurului asupra unui oraș specific.
13 | - **📊 Atribute disponibile**:
14 | - **Oraș**: Orașul monitorizat.
15 | - **Județ**: Județul în care se află orașul.
16 | - **Distanță (km)**: Distanța față de epicentru.
17 | - **Accelerația maximă a solului (PGA)**: Mișcarea maximă a solului (procent din accelerația gravitațională).
18 | - **Viteza maximă a solului (PGV)**: Mișcarea maximă a solului în cm/s.
19 | - **Intensitate**: Gradul perceput al cutremurului.
20 | - **Intensitate accelerației**: Intensitatea resimțită a accelerației solului în orașul monitorizat.
21 |
22 |
23 | ### Senzor `Cutremur`:
24 | - **🔍 Monitorizare Generală**:
25 | - Urmărește datele generale despre ultimul cutremur detectat.
26 | - **📊 Atribute disponibile**:
27 | - **ID Eveniment**: ID-ul evenimentului seismic.
28 | - **Magnitudine (ML)**: Magnitudinea pe scara locală.
29 | - **Magnitudinea Momentului (Mw)**: Puterea reală a cutremurului.
30 | - **Ora locală**: Ora locală a evenimentului.
31 | - **Coordonate**: Latitudine și longitudine ale epicentrului.
32 | - **Adâncime (km)**: Adâncimea epicentrului.
33 | - **Zonă**: Zona epicentrului.
34 | - **Intensitate**: Intensitatea percepută.
35 | - **Alerta**: Indică dacă evenimentul este nou.
36 |
37 | ### Senzor `Record cutremur`:
38 | - **🔍 Monitorizare Generală**:
39 | - Urmărește și înregistrează detaliile celui mai mare cutremur detectat în ultima perioadă, bazat pe datele stocate în fișierul record.json.
40 | - **📊 Atribute disponibile**:
41 | - **ID Eveniment**: ID-ul evenimentului seismic.
42 | - **Magnitudine (ML)**: Magnitudinea pe scara locală.
43 | - **Magnitudinea Momentului (Mw)**: Puterea reală a cutremurului.
44 | - **Ora locală**: Ora locală a evenimentului.
45 | - **Coordonate**: Latitudine și longitudine ale epicentrului.
46 | - **Adâncime (km)**: Adâncimea epicentrului.
47 | - **Zonă**: Zona epicentrului.
48 | - **Intensitate**: Intensitatea percepută.
49 |
50 | ---
51 |
52 | ## ⚙️ Configurare
53 |
54 | ### 🛠️ Interfața UI:
55 | 1. Instalează integrarea prin HACS sau manual (vezi detaliile de mai jos).
56 | 2. Adaugă integrarea din meniul **Setări > Dispozitive și Servicii > Adaugă Integrare**.
57 | 3. Specifică intervalul de actualizare (în secunde, între `10` și `3600`).
58 | 4. Alege un oraș din lista disponibilă pentru monitorizare.
59 |
60 | ---
61 |
62 | ## 🚀 Instalare
63 |
64 | ### 💡 Instalare prin HACS:
65 | 1. Adaugă [depozitul personalizat](https://github.com/cnecrea/infpro) în HACS. 🛠️
66 | 2. Caută integrarea **Cutremur România (INFP)** și instaleaz-o. ✅
67 | 3. Repornește Home Assistant și configurează integrarea. 🔄
68 |
69 | ### ✋ Instalare manuală:
70 | 1. Clonează sau descarcă [depozitul GitHub](https://github.com/cnecrea/infpro). 📂
71 | 2. Copiază folderul `custom_components/infpro` în directorul `custom_components` al Home Assistant. 🗂️
72 | 3. Repornește Home Assistant și configurează integrarea. 🔧
73 |
74 | ---
75 |
76 | ## ✨ Exemple de utilizare
77 |
78 | ### 🔔 Automatizare bazată pe Magnitudine:
79 | Creează o automatizare pentru a primi notificări atunci când magnitudinea unui cutremur depășește un anumit prag.
80 |
81 | ```yaml
82 | alias: Notificare Cutremur
83 | description: Notificare dacă magnitudinea depășește 4.5 și alerta este "Da"
84 | trigger:
85 | - platform: state
86 | entity_id: sensor.cutremur
87 | attribute: "Alerta"
88 | to: "Da"
89 | condition:
90 | - condition: numeric_state
91 | entity_id: sensor.cutremur
92 | attribute: Magnitudine (ML)
93 | above: 4.5
94 | action:
95 | - service: notify.mobile_app_your_phone
96 | data:
97 | title: "Cutremur Detectat! 🌋"
98 | message: "Un cutremur cu magnitudinea {{ states('sensor.cutremur') }} a fost detectat."
99 | mode: single
100 | ```
101 |
102 | ### 🔍 Card pentru Dashboard:
103 | Afișează informații despre cutremure și impactul asupra unui oraș pe interfața Home Assistant.
104 |
105 | ```yaml
106 | type: entities
107 | title: Monitorizare Cutremure
108 | entities:
109 | - entity: sensor.cutremur
110 | name: Ultimul Cutremur
111 | - entity: sensor.date_analiza
112 | name: Date analiză
113 | ```
114 |
115 | ---
116 |
117 | ## ☕ Susține dezvoltatorul
118 |
119 | Dacă ți-a plăcut această integrare și vrei să sprijini munca depusă, **invită-mă la o cafea**! 🫶
120 | Nu costă nimic, iar contribuția ta ajută la dezvoltarea viitoare a proiectului. 🙌
121 |
122 | [](https://buymeacoffee.com/cnecrea)
123 |
124 | Mulțumesc pentru sprijin și apreciez fiecare gest de susținere! 🤗
125 |
126 | ---
127 |
128 | ## 🧑💻 Contribuții
129 |
130 | Contribuțiile sunt binevenite! Simte-te liber să trimiți un pull request sau să raportezi probleme [aici](https://github.com/cnecrea/infpro/issues).
131 |
132 | ---
133 |
134 | ## 🌟 Suport
135 | Dacă îți place această integrare, oferă-i un ⭐ pe [GitHub](https://github.com/cnecrea/infpro/)! 😊
136 |
--------------------------------------------------------------------------------
/custom_components/infpro/const.py:
--------------------------------------------------------------------------------
1 | # const.py
2 | DOMAIN = "infpro"
3 | UPDATE_INTERVAL = 180 # Intervalul implicit de actualizare (în secunde)
4 | DEFAULT_ORAS = "5" # Orașul implicit (Alba Iulia), din LISTA_ORASE
5 |
6 | PLATFORMS = ["sensor"]
7 |
8 |
9 | # URL-urile pentru API
10 | BASE_URL = "https://dev.syspro.ro"
11 | URL_CUTREMUR = f"{BASE_URL}/homeassistant/date_api.json"
12 |
13 | # Lista codurilor de județe
14 | LISTA_JUDET = [
15 | "AB", "AR", "AG", "BC", "BH", "BN", "BR", "BT", "BV", "BZ",
16 | "CS", "CL", "CJ", "CT", "CV", "DB", "DJ", "GL", "GR", "GJ",
17 | "HR", "HD", "IL", "IS", "IF", "MM", "MH", "MS", "NT", "OT",
18 | "PH", "SM", "SJ", "SB", "SV", "TR", "TM", "TL", "VS", "VL",
19 | "VN", "B"
20 | ]
21 |
22 | # Dicționar compact pentru coduri și numele complete ale județelor
23 | JUDETE_MAP = {
24 | "AB": "Alba", "AR": "Arad", "AG": "Argeș", "BC": "Bacău", "BH": "Bihor",
25 | "BN": "Bistrița-Năsăud", "BR": "Brăila", "BT": "Botoșani", "BV": "Brașov", "BZ": "Buzău",
26 | "CS": "Caraș-Severin", "CL": "Călărași", "CJ": "Cluj", "CT": "Constanța", "CV": "Covasna",
27 | "DB": "Dâmbovița", "DJ": "Dolj", "GL": "Galați", "GR": "Giurgiu", "GJ": "Gorj",
28 | "HR": "Harghita", "HD": "Hunedoara", "IL": "Ialomița", "IS": "Iași", "IF": "Ilfov",
29 | "MM": "Maramureș", "MH": "Mehedinți", "MS": "Mureș", "NT": "Neamț", "OT": "Olt",
30 | "PH": "Prahova", "SM": "Satu Mare", "SJ": "Sălaj", "SB": "Sibiu", "SV": "Suceava",
31 | "TR": "Teleorman", "TM": "Timiș", "TL": "Tulcea", "VS": "Vaslui", "VL": "Vâlcea",
32 | "VN": "Vrancea", "B": "București"
33 | }
34 |
35 | LISTA_ORASE = [
36 | "1: Abrud", "2: Adjud", "3: Agnita", "4: Aiud", "5: Alba Iulia",
37 | "6: Albesti", "7: Alesd", "8: Alexandria", "9: Amara", "10: Anina",
38 | "11: Aninoasa", "12: Arad", "13: Ardud", "14: Avrig", "15: Azuga",
39 | "16: Babadag", "17: Babeni", "18: Bacau", "19: Baia De Arama", "20: Baia De Aries",
40 | "21: Baia Mare", "22: Baia Sprie", "23: Baicoi", "24: Baile Herculane", "25: Baile Olanesti",
41 | "26: Baile Tusnad", "27: Bailesti", "28: Balan", "29: Bals", "30: Baneasa",
42 | "31: Baraolt", "32: Barlad", "33: Basarabi", "34: Bechet", "35: Beclean",
43 | "36: Beius", "37: Berbesti", "38: Beresti", "39: Bicaz", "40: Bistrita",
44 | "41: Blaj", "42: Bocsa", "43: Boldesti-Scaeni", "44: Bolintin-Vale", "45: Borsa",
45 | "46: Borsec", "47: Botosani", "48: Brad", "49: Bragadiru", "50: Braila",
46 | "51: Brasov", "52: Brezoi", "53: Brosteni", "54: Bucecea", "55: Bucuresti",
47 | "56: Budesti", "57: Buftea", "58: Buhusi", "59: Bumbesti-Jiu", "60: Busteni",
48 | "61: Buzau", "62: Buzias", "63: Cajvana", "64: Calafat", "65: Calarasi",
49 | "66: Calimanesti-Caciulata", "67: Campeni", "68: Campia Turzii", "69: Campina", "70: Campulung",
50 | "71: Campulung Moldovenesc", "72: Caracal", "73: Caransebes", "74: Carei", "75: Cavnic",
51 | "76: Cazanesti", "77: Cehu Silvaniei", "78: Cernavoda", "79: Chisineu-Cris", "80: Chitila",
52 | "81: Ciacova", "82: Cisnadie", "83: Cluj-Napoca", "84: Codlea", "85: Comanesti",
53 | "86: Comarnic", "87: Constanta", "88: Copsa Mica", "89: Corabia", "90: Costesti",
54 | "91: Covasna", "92: Craiova", "93: Cristuru Secuiesc", "94: Cugir", "95: Curtea De Arges",
55 | "96: Curtici", "97: Dabuleni", "98: Darmanesti", "99: Dej", "100: Deta",
56 | "101: Deva", "102: Dolhasca", "103: Dorohoi", "104: Draganesti-Olt", "105: Dragasani",
57 | "106: Dragomiresti", "107: Drobeta-Turnu Severin", "108: Dumbraveni", "109: Fagaras", "110: Faget",
58 | "111: Falticeni", "112: Faurei", "113: Fetesti", "114: Fieni", "115: Fierbinti-Targ",
59 | "116: Filiasi", "117: Flamanzi", "118: Focsani", "119: Frasin", "120: Fundulea",
60 | "121: Gaesti", "122: Galati", "123: Gataia", "124: Geoagiu", "125: Gheorgheni",
61 | "126: Gherla", "127: Ghimbav", "128: Giurgiu", "129: Gura Humorului", "130: Harlau",
62 | "131: Harsova", "132: Hateg", "133: Horezu", "134: Huedin", "135: Hunedoara",
63 | "136: Ianca", "137: Iasi", "138: Iernut", "139: Ineu", "140: Insuratei",
64 | "141: Intorsura Buzaului", "142: Isaccea", "143: Jibou", "144: Jimbolia", "145: Lehliu-Gara",
65 | "146: Lipova", "147: Liteni", "148: Livada", "149: Ludus", "150: Lugoj",
66 | "151: Lupeni-Hr", "152: Lupeni-Hu", "153: Macin", "154: Magurele", "155: Mangalia",
67 | "156: Marasesti", "157: Marghita", "158: Medgidia", "159: Medias", "160: Miercurea Ciuc",
68 | "161: Miercurea Nirajului", "162: Miercurea Sibiului", "163: Mihailesti", "164: Milisauti", "165: Mioveni",
69 | "166: Mizil", "167: Moinesti", "168: Moldova Noua", "169: Moreni", "170: Motru",
70 | "171: Murgeni", "172: Nadlac", "173: Nasaud", "174: Navodari", "175: Negresti",
71 | "176: Negresti-Oas", "177: Negru Voda", "178: Nehoiu", "179: Novaci", "180: Nucet",
72 | "181: Ocna Mures", "182: Ocna Sibiului", "183: Ocnele Mari", "184: Odobesti", "185: Odorheiu Secuiesc",
73 | "186: Oltenita", "187: Onesti", "188: Oradea", "189: Orastie", "190: Oravita",
74 | "191: Orsova", "192: Otelu Rosu", "193: Otopeni", "194: Ovidiu", "195: Panciu",
75 | "196: Pancota", "197: Pantelimon", "198: Pascani", "199: Patarlagele", "200: Pecica",
76 | "201: Petrila", "202: Petrosani", "203: Piatra Neamt", "204: Piatra Olt", "205: Pitesti",
77 | "206: Ploiesti", "207: Plopeni", "208: Pogoanele", "209: Potcoava", "210: Predeal",
78 | "211: Pucioasa", "212: Racari", "213: Radauti", "214: Ramnicu Sarat", "215: Ramnicu Valcea",
79 | "216: Rasnov", "217: Recas", "218: Reghin", "219: Resita", "220: Roman",
80 | "221: Rosiori De Vede", "222: Rovinari", "223: Roznov", "224: Rupea", "225: Sacele",
81 | "226: Salcea", "227: Saliste", "228: Salistea De Sus", "229: Salonta", "230: Sangeorgiu De Mures",
82 | "231: Sangeorgiu De Padure", "232: Sannicolau Mare", "233: Santana", "234: Sarmasu", "235: Satu Mare",
83 | "236: Saveni", "237: Scornicesti", "238: Sebes", "239: Sebis", "240: Segarcea",
84 | "241: Seini", "242: Sfantu Gheorghe", "243: Sibiu", "244: Sighetu Marmatiei", "245: Sighisoara",
85 | "246: Simeria", "247: Simleu Silvaniei", "248: Sinaia", "249: Siret", "250: Slanic",
86 | "251: Slanic-Moldova", "252: Slatina", "253: Slobozia", "254: Solca", "255: Somcuta Mare",
87 | "256: Sovata", "257: Stefanesti-Bt", "258: Stefanesti-Ag", "259: Stei", "260: Strehaia",
88 | "261: Suceava", "262: Talmaciu", "263: Tandarei", "264: Targoviste", "265: Targu Bujor",
89 | "266: Targu Carbunesti", "267: Targu Frumos", "268: Targu Jiu", "269: Targu Lapus", "270: Targu Mures",
90 | "271: Targu Ocna", "272: Targu Secuiesc", "273: Targu-Neamt", "274: Tarnaveni", "275: Tasnad",
91 | "276: Tautii-Magheraus", "277: Techirghiol", "278: Tecuci", "279: Teius", "280: Ticleni",
92 | "281: Timisoara", "282: Tismana", "283: Titu", "284: Toplita", "285: Topoloveni",
93 | "286: Tulcea", "287: Turceni", "288: Turda", "289: Turnu Magurele", "290: Ulmeni",
94 | "291: Ungheni", "292: Uricani", "293: Urziceni", "294: Valea Lui Mihai", "295: Valenii De Munte",
95 | "296: Vanju Mare", "297: Vascau", "298: Vaslui", "299: Vatra Dornei", "300: Vicovu De Jos",
96 | "301: Vicovu De Sus", "302: Victoria", "303: Videle", "304: Viseu De Sus", "305: Vlahita",
97 | "306: Voluntari", "307: Vulcan", "308: Zalau", "309: Zarnesti", "310: Zimnicea", "311: Zlatna"
98 | ]
99 |
100 | INTENSITY_MAP = {
101 | "I": "Neresimțită", "I-II": "Neresimțită Slabă", "II": "Slabă", "III": "Slabă",
102 | "IV": "Ușoară", "V": "Moderată", "VI": "Puternică", "VII": "Foarte puternică",
103 | "VIII": "Severă", "IX": "Violentă", "X": "Extremă", "XI": "Catastrofală", "XII": "Apocaliptică"
104 | }
105 |
106 | ATTRIBUTION = "Date furnizate de Institutul Național de Cercetare și Dezvoltare pentru Fizica Pământului"
107 |
--------------------------------------------------------------------------------
/custom_components/infpro/sensor.py:
--------------------------------------------------------------------------------
1 |
2 | import logging
3 | import os
4 | import aiofiles
5 | from datetime import timedelta
6 |
7 | from homeassistant.components.sensor import SensorEntity
8 | from homeassistant.helpers.device_registry import DeviceEntryType
9 | from homeassistant.helpers.update_coordinator import CoordinatorEntity
10 | from homeassistant.util import Throttle
11 |
12 | from .const import DOMAIN, ATTRIBUTION, INTENSITY_MAP, JUDETE_MAP
13 |
14 | _LOGGER = logging.getLogger(__name__)
15 |
16 |
17 | async def async_setup_entry(hass, config_entry, async_add_entities):
18 | """Configurează intrarea pentru integrarea INFP."""
19 | coordinator = hass.data[DOMAIN][config_entry.entry_id]["coordinator"]
20 | oras_id = config_entry.data.get("oras_id")
21 | oras_nume = config_entry.data.get("oras_nume")
22 |
23 | # 1) Senzorul principal (CutremurSensor)
24 | cutremur_sensor = CutremurSensor(coordinator, oras_id, oras_nume)
25 |
26 | # 2) Senzorul pentru datele istorice (RecordCutremurSensor)
27 | record_sensor = RecordCutremurSensor(coordinator) # Corectat: eliminată referința la `hass`
28 |
29 | # 3) Senzorul pentru analiza impactului în orașe (AnalizaDate)
30 | analiza_date_sensor = AnalizaDate(coordinator, oras_id, oras_nume)
31 |
32 | # Adaugă toate entitățile
33 | async_add_entities([cutremur_sensor, record_sensor, analiza_date_sensor])
34 |
35 | _LOGGER.debug(
36 | "Senzorii pentru INFP au fost configurați: Oras ID=%s, Oras Nume=%s. "
37 | "Senzorii Cutremur, RecordCutremur și AnalizaDate au fost adăugați.",
38 | oras_id,
39 | oras_nume,
40 | )
41 |
42 |
43 | # ------------------------------------------------------------------------
44 | # CutremurSensor
45 | # ------------------------------------------------------------------------
46 | class CutremurSensor(CoordinatorEntity, SensorEntity):
47 | """Reprezentarea senzorului principal pentru cutremure."""
48 |
49 | def __init__(self, coordinator, oras_id, oras_nume):
50 | """Inițializează senzorul."""
51 | super().__init__(coordinator)
52 | self._oras_id = oras_id
53 | self._oras_nume = oras_nume
54 | self._attr_name = "Cutremur"
55 | self._attr_unique_id = f"{DOMAIN}_cutremur"
56 | self._attributes = {"status": "Date în curs de actualizare"}
57 | self._alerta = "Nu"
58 |
59 | _LOGGER.debug(
60 | "Senzor Cutremur inițializat: ID=%s, Nume oraș=%s",
61 | self._attr_unique_id,
62 | self._oras_nume,
63 | )
64 |
65 | async def async_added_to_hass(self):
66 | """Se apelează când entitatea este adăugată în Home Assistant."""
67 | await super().async_added_to_hass()
68 |
69 | def _schedule_coordinator_update():
70 | self.hass.async_create_task(self._async_handle_coordinator_update())
71 |
72 | self.async_on_remove(
73 | self.coordinator.async_add_listener(_schedule_coordinator_update)
74 | )
75 |
76 | await self._async_handle_coordinator_update()
77 |
78 | async def _async_handle_coordinator_update(self):
79 | """Actualizează datele senzorului."""
80 | data = self.coordinator.data
81 |
82 | if not data or "date_cutremur" not in data:
83 | _LOGGER.debug("Nu există date valide în coordinator pentru cutremur.")
84 | self._attributes = {"status": "Date în curs de actualizare"}
85 | self.async_write_ha_state()
86 | return
87 |
88 | event_data = data["date_cutremur"]
89 |
90 | # Actualizăm atributele senzorului cu informațiile din `date_cutremur`
91 | self._attributes = {
92 | "ID eveniment": event_data.get("smevid", "N/A"),
93 | "Magnitudine (ML)": event_data.get("mag_ml", "N/A"),
94 | "Magnitudinea Momentului (Mw)": event_data.get("mag_mw", "N/A"),
95 | "Ora locală": event_data.get("local_time", "N/A"),
96 | "Latitudine": event_data.get("elat", "N/A"),
97 | "Longitudine": event_data.get("elon", "N/A"),
98 | "Adâncime (km)": event_data.get("depth", "N/A"),
99 | "Zonă": event_data.get("location", "N/A"),
100 | "Intensitate": INTENSITY_MAP.get(event_data.get("intensity", "N/A"), "Necunoscută"),
101 | "Alerta": self._alerta,
102 | "attribution": ATTRIBUTION,
103 | }
104 |
105 | self._alerta = "Da" if event_data.get("smevid") != self._attributes.get("ID eveniment") else "Nu"
106 | self.async_write_ha_state()
107 |
108 | @property
109 | def native_value(self):
110 | """Returnează valoarea principală a senzorului (magnitudinea ML)."""
111 | data = self.coordinator.data
112 | if data and "date_cutremur" in data:
113 | event_data = data["date_cutremur"]
114 | return event_data.get("mag_ml", "N/A")
115 | return "N/A"
116 |
117 | @property
118 | def extra_state_attributes(self):
119 | """Returnează atributele suplimentare pentru senzor."""
120 | return self._attributes
121 |
122 | @property
123 | def icon(self):
124 | """Pictograma senzorului."""
125 | return "mdi:waves"
126 |
127 | @property
128 | def device_info(self):
129 | """Informații despre dispozitiv."""
130 | return {
131 | "identifiers": {(DOMAIN, "cutremur")},
132 | "name": "Cutremur România (INFP)",
133 | "manufacturer": "Institutul Național pentru Fizica Pământului",
134 | "model": "Monitorizare Seisme",
135 | "entry_type": DeviceEntryType.SERVICE,
136 | }
137 |
138 |
139 |
140 |
141 | # ------------------------------------------------------------------------
142 | # RecordCutremurSensor
143 | # ------------------------------------------------------------------------
144 | class RecordCutremurSensor(CoordinatorEntity, SensorEntity):
145 | """
146 | Senzor secundar care preia datele din `record_cutremur` din API.
147 | """
148 |
149 | def __init__(self, coordinator):
150 | """Inițializează senzorul."""
151 | super().__init__(coordinator)
152 | self._attr_name = "Record cutremur"
153 | self._attr_unique_id = f"{DOMAIN}_record_cutremur"
154 | self._state = "N/A" # Magnitudinea ML
155 | self._attributes = {"status": "Date în curs de actualizare"}
156 | self._available = True
157 |
158 | async def async_added_to_hass(self):
159 | """Se apelează când entitatea este adăugată în Home Assistant."""
160 | await super().async_added_to_hass()
161 |
162 | def _schedule_coordinator_update():
163 | self.hass.async_create_task(self._async_handle_coordinator_update())
164 |
165 | self.async_on_remove(
166 | self.coordinator.async_add_listener(_schedule_coordinator_update)
167 | )
168 |
169 | await self._async_handle_coordinator_update()
170 |
171 | async def _async_handle_coordinator_update(self):
172 | """Actualizează senzorul cu datele din API."""
173 | data = self.coordinator.data
174 |
175 | if not data or "record_cutremur" not in data:
176 | _LOGGER.debug("Nu există date valide pentru record_cutremur.")
177 | self._state = "N/A"
178 | self._attributes = {"status": "Date în curs de actualizare"}
179 | self.async_write_ha_state()
180 | return
181 |
182 | record_data = data["record_cutremur"]
183 |
184 | # Actualizăm atributele senzorului cu informațiile din `record_cutremur`
185 | self._state = record_data.get("new_mag_ml", "N/A")
186 | self._attributes = {
187 | "ID eveniment": record_data.get("new_smevid", "N/A"),
188 | "Magnitudine (ML)": record_data.get("new_mag_ml", "N/A"),
189 | "Magnitudinea Momentului (Mw)": record_data.get("mag_mw", "N/A"),
190 | "Ora locală": record_data.get("local_time", "N/A"),
191 | "Latitudine": record_data.get("elat", "N/A"),
192 | "Longitudine": record_data.get("elon", "N/A"),
193 | "Adâncime (km)": record_data.get("depth", "N/A"),
194 | "Zonă": record_data.get("location", "N/A"),
195 | "Intensitate": INTENSITY_MAP.get(record_data.get("intensity", "N/A"), "Necunoscută"),
196 | "attribution": ATTRIBUTION,
197 | }
198 |
199 | _LOGGER.debug(
200 | "RecordCutremurSensor a încărcat date: ID=%s, ML=%s",
201 | self._attributes.get("ID eveniment", "N/A"),
202 | self._state,
203 | )
204 |
205 | self._available = True
206 | self.async_write_ha_state()
207 |
208 | @property
209 | def native_value(self):
210 | """Returnează valoarea principală a senzorului (magnitudinea ML)."""
211 | return self._state
212 |
213 | @property
214 | def extra_state_attributes(self):
215 | """Returnează atributele suplimentare pentru senzor."""
216 | return self._attributes
217 |
218 | @property
219 | def available(self):
220 | """Returnează dacă senzorul este disponibil."""
221 | return self._available
222 |
223 | @property
224 | def icon(self):
225 | """Pictograma senzorului."""
226 | return "mdi:waves-arrow-up"
227 |
228 | @property
229 | def device_info(self):
230 | """Informații despre dispozitiv."""
231 | return {
232 | "identifiers": {(DOMAIN, "cutremur")},
233 | "name": "Cutremur România (INFP)",
234 | "manufacturer": "Institutul Național pentru Fizica Pământului",
235 | "model": "Monitorizare Seisme",
236 | "entry_type": DeviceEntryType.SERVICE,
237 | }
238 |
239 |
240 | # ------------------------------------------------------------------------
241 | # AnalizaDate
242 | # ------------------------------------------------------------------------
243 | class AnalizaDate(CoordinatorEntity, SensorEntity):
244 | """Senzor care folosește datele din analiza_cutremur."""
245 |
246 | def __init__(self, coordinator, oras_id, oras_nume):
247 | """Inițializează senzorul DateAnaliza."""
248 | super().__init__(coordinator)
249 | self._oras_id = oras_id
250 | self._oras_nume = oras_nume
251 | self._attr_name = "Analiză date"
252 | self._attr_unique_id = f"{DOMAIN}_analiza_date_{oras_id}"
253 | self._attributes = {}
254 | self._available = True
255 |
256 | async def async_added_to_hass(self):
257 | """Se apelează când entitatea este adăugată în Home Assistant."""
258 | await super().async_added_to_hass()
259 |
260 | def _schedule_coordinator_update():
261 | self.hass.async_create_task(self._async_handle_coordinator_update())
262 |
263 | self.async_on_remove(
264 | self.coordinator.async_add_listener(_schedule_coordinator_update)
265 | )
266 |
267 | await self._async_handle_coordinator_update()
268 |
269 | async def _async_handle_coordinator_update(self):
270 | """Actualizează datele senzorului."""
271 | data = self.coordinator.data
272 |
273 | if not data or "analiza_cutremur" not in data:
274 | self._attributes = {"status": "Date în curs de actualizare"}
275 | self._available = True # Senzorul rămâne disponibil
276 | self.async_write_ha_state()
277 | return
278 |
279 | analiza = data["analiza_cutremur"]
280 |
281 | if not isinstance(analiza, list) or not analiza:
282 | self._attributes = {"status": "Date în curs de actualizare"}
283 | self._available = True # Senzorul rămâne disponibil
284 | self.async_write_ha_state()
285 | return
286 |
287 | # Găsește datele pentru orașul specificat (oras_id)
288 | oras_data = next((item for item in analiza if str(item.get("oras_id")) == str(self._oras_id)), None)
289 |
290 | if not oras_data:
291 | self._attributes = {"status": "Date în curs de actualizare"}
292 | self._available = True # Senzorul rămâne disponibil
293 | self.async_write_ha_state()
294 | return
295 |
296 | # Populează atributele senzorului cu informațiile relevante
297 | self._attributes = {
298 | "Oraș": oras_data.get("oras", "N/A"),
299 | "Județ": JUDETE_MAP.get(oras_data.get("judet", "N/A"), "N/A"),
300 | "Distanță (km)": oras_data.get("distanta_km", "N/A"),
301 | "Accelerația maximă a solului": oras_data.get("pga", "N/A"),
302 | "Viteza maximă a solului": oras_data.get("pgv", "N/A"),
303 | "Intensitate": INTENSITY_MAP.get(oras_data.get("intensitate", "N/A"), "Necunoscută"),
304 | "Intensitatea accelerației": oras_data.get("iacc", "N/A"),
305 | "attribution": ATTRIBUTION,
306 | }
307 | self._available = True
308 | self.async_write_ha_state()
309 |
310 | @property
311 | def native_value(self):
312 | """Returnează valoarea principală a senzorului (numele orașului)."""
313 | return self._oras_nume
314 |
315 | @property
316 | def extra_state_attributes(self):
317 | """Returnează atributele suplimentare pentru senzor."""
318 | return self._attributes
319 |
320 | @property
321 | def available(self):
322 | """Returnează dacă senzorul este disponibil."""
323 | return self._available
324 |
325 | @property
326 | def icon(self):
327 | """Pictograma senzorului."""
328 | return "mdi:chart-bar"
329 |
330 | @property
331 | def device_info(self):
332 | """Informații despre dispozitiv."""
333 | return {
334 | "identifiers": {(DOMAIN, "cutremur")},
335 | "name": "Cutremur România (INFP)",
336 | "manufacturer": "Institutul Național pentru Fizica Pământului",
337 | "model": "Monitorizare Seisme",
338 | "entry_type": DeviceEntryType.SERVICE,
339 | }
340 |
--------------------------------------------------------------------------------