diff --git a/custom-component/space_weather/sensor.py b/custom-component/space_weather/sensor.py index 0b02b24..ccbd506 100644 --- a/custom-component/space_weather/sensor.py +++ b/custom-component/space_weather/sensor.py @@ -1,10 +1,7 @@ import logging -from datetime import timedelta +from datetime import timedelta, datetime import aiohttp -import homeassistant.helpers.config_validation as cv -import voluptuous as vol -from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle @@ -14,23 +11,19 @@ _LOGGER = logging.getLogger(__name__) CONF_URL = "https://services.swpc.noaa.gov/products/noaa-scales.json" SCAN_INTERVAL = timedelta(minutes=30) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional("url", default=CONF_URL): cv.string, -}) - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): - url = config.get("url") session = async_get_clientsession(hass) async_add_entities([ - SpaceWeatherScaleSensor(session, url, "R"), - SpaceWeatherScaleSensor(session, url, "S"), - SpaceWeatherScaleSensor(session, url, "G"), - SpaceWeatherPredictionSensor(session, url, "R", "MinorProb", "pred_r_minor"), - SpaceWeatherPredictionSensor(session, url, "R", "MajorProb", "pred_r_major"), - SpaceWeatherPredictionSensor(session, url, "S", "Scale", "pred_s_scale"), - SpaceWeatherPredictionSensor(session, url, "S", "Prob", "pred_s_prob"), - SpaceWeatherPredictionSensor(session, url, "G", "Scale", "pred_g_scale"), + SpaceWeatherScaleSensor(session, CONF_URL, "R"), + SpaceWeatherScaleSensor(session, CONF_URL, "S"), + SpaceWeatherScaleSensor(session, CONF_URL, "G"), + SpaceWeatherPredictionSensor(session, CONF_URL, "R", "MinorProb", "pred_r_minor"), + SpaceWeatherPredictionSensor(session, CONF_URL, "R", "MajorProb", "pred_r_major"), + SpaceWeatherPredictionSensor(session, CONF_URL, "S", "Scale", "pred_s_scale"), + SpaceWeatherPredictionSensor(session, CONF_URL, "S", "Prob", "pred_s_prob"), + SpaceWeatherPredictionSensor(session, CONF_URL, "G", "Scale", "pred_g_scale"), + SpaceWeatherDateStampSensor(session, CONF_URL), ], True) @@ -129,9 +122,64 @@ class SpaceWeatherPredictionSensor(Entity): async with self._session.get(self._url) as response: if response.status == 200: data = await response.json() - self._data = data["1"] + now = datetime.now() + timedelta(days=1) + tomorrow_date = now.strftime('%Y-%m-%d') + tomorrow_data = {} + for k, v in data.items(): + datestamp = v['DateStamp'] + if datestamp == tomorrow_date: + tomorrow_data = v + assert len(tomorrow_data.keys()) is not None + self._data = tomorrow_data self._state = self._data[self._scale_key][self._pred_key] else: _LOGGER.error(f"Error fetching data from {self._url}") except aiohttp.ClientError as err: _LOGGER.error(f"Error fetching data from {self._url}: {err}") + + +class SpaceWeatherDateStampSensor(Entity): + """ + Attributes don't seem to be working so we use a single sensor to track the timestamp of the space weather + prediction updated. + """ + + def __init__(self, session, url): + self._session = session + self._url = url + self._name = "Space Weather Prediction Date Stamp" + self._state = None + self._data = None + + @property + def name(self): + return self._name + + @property + def unique_id(self): + return "space_weather_prediction_date_stamp" + + @property + def state(self): + return self._state + + @Throttle(SCAN_INTERVAL) + async def async_update(self): + try: + async with self._session.get(self._url) as response: + if response.status == 200: + data = await response.json() + now = datetime.now() + timedelta(days=1) + tomorrow_date = now.strftime('%Y-%m-%d') + tomorrow_data = {} + for k, v in data.items(): + datestamp = v['DateStamp'] + if datestamp == tomorrow_date: + tomorrow_data = v + assert len(tomorrow_data.keys()) is not None + self._data = tomorrow_data + self._state = datetime.strptime(f'{self._data["DateStamp"]} {self._data["TimeStamp"]}', "%Y-%m-%d %H:%M:%S").strftime('%m-%d-%Y %H:%M') + else: + _LOGGER.error(f"Error fetching data from {self._url}") + except aiohttp.ClientError as err: + _LOGGER.error(f"Error fetching data from {self._url}: {err}") diff --git a/dashboard/www/space-weather-pred-card.js b/dashboard/www/space-weather-pred-card.js index 238c24d..333c946 100644 --- a/dashboard/www/space-weather-pred-card.js +++ b/dashboard/www/space-weather-pred-card.js @@ -29,6 +29,7 @@ class SpaceWeatherPredictionCard extends HTMLElement { .prediction-item { text-align: center; margin-bottom: 16px; + cursor: pointer; } .prediction-label { @@ -66,9 +67,16 @@ class SpaceWeatherPredictionCard extends HTMLElement { .card-header { font-size: 20px; font-weight: bold; - padding: 16px; + padding: 0 16px 2px 16px; + text-align: center; + } + + .card-subheader { + padding: 0 16px 16px 16px; margin-bottom: 16px; text-align: center; + font-style: italic; + font-size: 15px; } .scale-value { @@ -87,9 +95,12 @@ class SpaceWeatherPredictionCard extends HTMLElement {
Space Weather Predictions
+
+ ${this._getStateValue('sensor.space_weather_prediction_date_stamp')} +
-
+
R1-R2
@@ -97,7 +108,7 @@ class SpaceWeatherPredictionCard extends HTMLElement { ${Math.round(parseFloat(this._getStateValue('sensor.space_weather_prediction_r_minorprob')))}%
-
+
R3-R5
@@ -105,7 +116,7 @@ class SpaceWeatherPredictionCard extends HTMLElement { ${Math.round(parseFloat(this._getStateValue('sensor.space_weather_prediction_r_majorprob')))}%
-
+
S1 or Greater
@@ -116,10 +127,10 @@ class SpaceWeatherPredictionCard extends HTMLElement { -
+
G Scale
G${this._getStateValue('sensor.space_weather_prediction_g_scale')} @@ -129,6 +140,7 @@ class SpaceWeatherPredictionCard extends HTMLElement {
`; + this._attachClickListeners(); } _getStateValue(entityId) { @@ -136,6 +148,11 @@ class SpaceWeatherPredictionCard extends HTMLElement { return state ? state.state : ''; } + _getAttribute(entityId, attribute) { + const state = this._hass.states[entityId]; + return state ? state.attributes[attribute] : ''; + } + _getNumericState(entityId) { const stateValue = this._getStateValue(entityId); return stateValue.substring(1); @@ -144,6 +161,22 @@ class SpaceWeatherPredictionCard extends HTMLElement { getCardSize() { return 5; } + + _attachClickListeners() { + const scaleItems = this.shadowRoot.querySelectorAll('.prediction-item'); + scaleItems.forEach(item => { + item.addEventListener('click', () => { + const entityId = item.dataset.entityId; + this._handleClick(entityId); + }); + }); + } + + _handleClick(entityId) { + const event = new Event('hass-more-info', {composed: true}); + event.detail = {entityId}; + this.dispatchEvent(event); + } } customElements.define('space-weather-prediction-card', SpaceWeatherPredictionCard);