├── .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 | ![logo](https://github.com/user-attachments/assets/4bef00a2-ff4b-4494-9d8a-ed3740671c04) 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 | [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Susține%20dezvoltatorul-orange?style=for-the-badge&logo=buy-me-a-coffee)](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 | --------------------------------------------------------------------------------