-
-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Added DWD WarnApp Sensor #8657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added DWD WarnApp Sensor #8657
Changes from 7 commits
1af723f
7cb7924
17cc1a7
7b3e84c
a85e8d5
d9da925
d2f6676
df60b1a
deb31e7
5c270a4
7426e3e
43cc842
8a510f7
339b097
8095761
0e0a9e9
70d0195
0940766
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,236 @@ | ||
| """ | ||
| Support for getting statistical data from a DWD Weather Warnings. | ||
| For more details about this platform, please refer to the documentation at | ||
| https://home-assistant.io/components/sensor.dwd_weather_warnings/ | ||
|
|
||
| Data is fetched from DWD: | ||
| https://rcccm.dwd.de/DE/wetter/warnungen_aktuell/objekt_einbindung/objekteinbindung.html | ||
|
|
||
| Warnungen vor extremem Unwetter (Stufe 4) | ||
| Unwetterwarnungen (Stufe 3) | ||
| Warnungen vor markantem Wetter (Stufe 2) | ||
| Wetterwarnungen (Stufe 1) | ||
| """ | ||
| import logging | ||
| import json | ||
| from datetime import timedelta | ||
|
|
||
| import voluptuous as vol | ||
|
|
||
| import homeassistant.helpers.config_validation as cv | ||
| from homeassistant.helpers.entity import Entity | ||
| from homeassistant.components.sensor import PLATFORM_SCHEMA | ||
| from homeassistant.const import ( | ||
| CONF_NAME, CONF_MONITORED_CONDITIONS) | ||
| from homeassistant.util import Throttle | ||
| import homeassistant.util.dt as dt_util | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| DEFAULT_NAME = 'DWD-Wetter-Warnungen' | ||
|
|
||
| CONF_REGION_NAME = 'region_name' | ||
| DEFAULT_REGION_NAME = 'Hansestadt Hamburg' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This default does only make sense for a small group of users.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will remove the default region |
||
|
|
||
| SCAN_INTERVAL = timedelta(minutes=15) | ||
|
|
||
| MONITORED_CONDITIONS = { | ||
| 'current_warning_level': ['Current Warning Level', | ||
| None, 'mdi:close-octagon-outline'], | ||
| 'advance_warning_level': ['Advance Warning Level', | ||
| None, 'mdi:close-octagon-outline'], | ||
| } | ||
|
|
||
| PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | ||
| vol.Optional(CONF_REGION_NAME, default=DEFAULT_REGION_NAME): cv.string, | ||
| vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, | ||
| vol.Optional(CONF_MONITORED_CONDITIONS, default=MONITORED_CONDITIONS): | ||
| vol.All(cv.ensure_list, [vol.In(MONITORED_CONDITIONS)]), | ||
| }) | ||
|
|
||
|
|
||
| def setup_platform(hass, config, add_devices, discovery_info=None): | ||
| """Set up the DWD-Weather-Warnings sensor.""" | ||
| name = config.get(CONF_NAME) | ||
| region_name = config.get(CONF_REGION_NAME) | ||
|
|
||
| api = DwdWeatherWarningsAPI(region_name) | ||
|
|
||
| sensors = [DwdWeatherWarningsSensor(hass, api, name, condition) | ||
| for condition in config[CONF_MONITORED_CONDITIONS]] | ||
|
|
||
| add_devices(sensors, True) | ||
|
|
||
|
|
||
| class DwdWeatherWarningsSensor(Entity): | ||
| """Representation of a DWD-Weather-Warnings sensor.""" | ||
|
|
||
| def __init__(self, hass, api, name, variable): | ||
| """Initialize a DWD-Weather-Warnings sensor.""" | ||
| self._hass = hass | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You doesn't seems to use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will remove it |
||
| self._api = api | ||
| self._name = name | ||
| self._var_id = variable | ||
|
|
||
| variable_info = MONITORED_CONDITIONS[variable] | ||
| self._var_name = variable_info[0] | ||
| self._var_units = variable_info[1] | ||
| self._var_icon = variable_info[2] | ||
|
|
||
| @property | ||
| def name(self): | ||
| """Return the name of the sensor.""" | ||
| return "{} {}".format(self._name, self._var_name) | ||
|
|
||
| @property | ||
| def icon(self): | ||
| """Icon to use in the frontend, if any.""" | ||
| return self._var_icon | ||
|
|
||
| @property | ||
| def unit_of_measurement(self): | ||
| """Return the unit the value is expressed in.""" | ||
| return self._var_units | ||
|
|
||
| # pylint: disable=no-member | ||
| @property | ||
| def state(self): | ||
| """Return the state of the device.""" | ||
| try: | ||
| return round(self._api.data[self._var_id], 2) | ||
| except TypeError: | ||
| return self._api.data[self._var_id] | ||
|
|
||
| # pylint: disable=no-member | ||
| @property | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. redefinition of unused 'device_state_attributes' from line 96 |
||
| def device_state_attributes(self): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add an attribution?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you mean? |
||
| """Return the state attributes of the DWD-Weather-Warnings.""" | ||
|
|
||
| data = {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch |
||
| data['region_name'] = self._api.region_name | ||
|
|
||
| if self._api.region_id is not None: | ||
| data['region_id'] = self._api.region_id | ||
|
|
||
| if self._api.region_state is not None: | ||
| data['region_state'] = self._api.region_state | ||
| # data['region_map_url'] = 'https://www.dwd.de/DWD/warnungen/warnapp_gemeinden/json/warnungen_gemeinde_map_' + str(data['region_state'].lower()) + '.png' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (165 > 79 characters) |
||
|
|
||
| if self._api.data['time'] is not None: | ||
| data['last_update'] = dt_util.as_local( | ||
| dt_util.utc_from_timestamp(self._api.data['time'] / 1000)) | ||
|
|
||
| if self._var_id == 'current_warning_level': | ||
| prefix = 'current' | ||
| elif self._var_id == 'advance_warning_level': | ||
| prefix = 'advance' | ||
|
|
||
| data['warning_count'] = self._api.data[prefix + '_warning_count'] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Local variable 'prefix' might be referenced before assignment. Compare L122, L124.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok I will add a else path and throw an exception there... |
||
| i = 0 | ||
| for event in self._api.data[prefix + '_warnings']: | ||
| i = i + 1 | ||
|
|
||
| data['warning_{}_name'.format(i)] = event['event'] | ||
| data['warning_{}_level'.format(i)] = event['level'] | ||
| data['warning_{}_type'.format(i)] = event['type'] | ||
| if len(event['headline']) > 0: | ||
| data['warning_{}_headline'.format(i)] = event['headline'] | ||
| if len(event['description']) > 0: | ||
| data['warning_{}_description'.format(i)] = event['description'] | ||
| if len(event['instruction']) > 0: | ||
| data['warning_{}_instruction'.format(i)] = event['instruction'] | ||
|
|
||
| if event['start'] is not None: | ||
| data['warning_{}_start'.format(i)] = dt_util.as_local( | ||
| dt_util.utc_from_timestamp(event['start'] / 1000)) | ||
|
|
||
| if event['end'] is not None: | ||
| data['warning_{}_end'.format(i)] = dt_util.as_local( | ||
| dt_util.utc_from_timestamp(event['end'] / 1000)) | ||
|
|
||
| return data | ||
|
|
||
| @property | ||
| def available(self): | ||
| """Could the device be accessed during the last update call.""" | ||
| return self._api.available | ||
|
|
||
| def update(self): | ||
| """Get the latest data from the DWD-Weather-Warnings API.""" | ||
| self._api.update() | ||
|
|
||
|
|
||
| class DwdWeatherWarningsAPI(object): | ||
| """Get the latest data and update the states.""" | ||
|
|
||
| def __init__(self, region_name): | ||
| """Initialize the data object.""" | ||
| from homeassistant.components.sensor.rest import RestData | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please place this in the file header.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just remove this blank line.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
| resource = "{}{}{}?{}".format( | ||
| 'https://', | ||
| 'www.dwd.de', | ||
| '/DWD/warnungen/warnapp_landkreise/json/warnings.json', | ||
| 'jsonp=loadWarnings' | ||
| ) | ||
|
|
||
| self._rest = RestData('GET', resource, None, None, None, True) | ||
| self.region_name = region_name | ||
| self.region_id = None | ||
| self.region_state = None | ||
| self.data = None | ||
| self.available = True | ||
| self.update() | ||
|
|
||
| @Throttle(SCAN_INTERVAL) | ||
| def update(self): | ||
| """Get the latest data from the DWD-Weather-Warnings.""" | ||
| try: | ||
| self._rest.update() | ||
|
|
||
| json_string = self._rest.data[24:len(self._rest.data) - 2] | ||
| json_obj = json.loads(json_string) | ||
|
|
||
| data = {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could save a line here. My IDE complains about. ;-) - data = {}
-
- data['time'] = json_obj['time']
+ data = {'time': json_obj['time']}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch |
||
|
|
||
| data['time'] = json_obj['time'] | ||
|
|
||
| for mykey, myvalue in {'current': 'warnings', 'advance': 'vorabInformation'}.items(): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (97 > 79 characters) |
||
|
|
||
| _LOGGER.debug("Found {} {} global DWD warnings".format( | ||
| len(json_obj[myvalue]), mykey)) | ||
|
|
||
| data['{}_warning_level'.format(mykey)] = 0 | ||
| my_warnings = [] | ||
|
|
||
| if self.region_id is not None: | ||
| # get a specific region_id | ||
| if self.region_id in json_obj[myvalue]: | ||
| my_warnings = json_obj[myvalue][self.region_id] | ||
|
|
||
| else: | ||
| # loop through all items to find warnings, region_id and region_state for region_name | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (105 > 79 characters) |
||
| for key in json_obj[myvalue]: | ||
| if json_obj[myvalue][key][0]['regionName'] != self.region_name: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (87 > 79 characters) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (87 > 79 characters) |
||
| continue | ||
| my_warnings = json_obj[myvalue][key] | ||
| self.region_id = key | ||
| self.region_state = json_obj[myvalue][key][0]['stateShort'] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (83 > 79 characters) |
||
| break | ||
|
|
||
| # Get max warning level | ||
| for event in my_warnings: | ||
| if(event['level'] >= data['{}_warning_level'.format(mykey)]): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (81 > 79 characters) |
||
| data['{}_warning_level'.format(mykey)] = event['level'] | ||
|
|
||
| data['{}_warning_count'.format(mykey)] = len(my_warnings) | ||
| data['{}_warnings'.format(mykey)] = my_warnings | ||
|
|
||
| _LOGGER.debug("Found {} {} local DWD warnings".format( | ||
| len(my_warnings), mykey)) | ||
|
|
||
| self.data = data | ||
| self.available = True | ||
| except TypeError: | ||
| _LOGGER.error("Unable to fetch data from DWD-Weather-Warnings") | ||
| self.available = False | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be in English. If the user can set
name:if he/she want it in German.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok