diff --git a/custom_components/openweathermaphistory/config_flow.py b/custom_components/openweathermaphistory/config_flow.py index 4bf7ab1..0d704c3 100644 --- a/custom_components/openweathermaphistory/config_flow.py +++ b/custom_components/openweathermaphistory/config_flow.py @@ -56,11 +56,21 @@ def __init__(self) -> None: # noqa: D107 self._data = {} self.selected = {} + def create_formula(self,sensor,sensorclass,stateclass): + """Create formula dictionary.""" + formula = {} + formula[CONF_NAME] = sensor + formula[CONF_FORMULA] = "{{ " + sensor + " }}" + formula[CONF_ATTRIBUTES] = '' + formula[CONF_SENSORCLASS] = sensorclass + formula[CONF_STATECLASS] = stateclass + formula[CONF_UID] = str(uuid.uuid4()) + return formula + async def async_step_user( self, user_input=None ): """Initiate flow via the user interface.""" - errors: dict[str, str] = {} if user_input is not None: await self.async_set_unique_id(str(uuid.uuid4())) @@ -72,8 +82,7 @@ async def async_step_user( errors[CONF_NAME] = "duplicate_name" if errors: break - if user_input[CONF_MAX_DAYS] < user_input[CONF_INTIAL_DAYS]: - user_input[CONF_MAX_DAYS] = user_input[CONF_INTIAL_DAYS] + user_input[CONF_MAX_DAYS] = max(user_input[CONF_MAX_DAYS], user_input[CONF_INTIAL_DAYS]) lat = user_input[CONF_LOCATION][CONF_LATITUDE] lon = user_input[CONF_LOCATION][CONF_LONGITUDE] @@ -89,15 +98,54 @@ async def async_step_user( if not errors: # Input is valid, set data. - self._data = user_input + self._data = {} + resources = [] + self._data.update(user_input) + resources.append(self.create_formula('daily_count','none','none')) + options = user_input.get('create_sensors') + + # now process the create sensor options to build the required sensors + for i in range(6): + if 'forecast_rain' in options: + resources.append(self.create_formula(f"forecast{i}rain", 'precipitation','measurement')) + if 'forecast_snow' in options: + resources.append(self.create_formula(f"forecast{i}snow", 'precipitation','measurement')) + if 'forecast_max' in options: + resources.append(self.create_formula(f"forecast{i}max", 'temperature','measurement')) + if 'forecast_min' in options: + resources.append(self.create_formula(f"forecast{i}min", 'temperature','measurement')) + if 'forecast_humidity' in options: + resources.append(self.create_formula(f"forecast{i}humidity", 'humidity','measurement')) + if 'forecast_pop' in options: + resources.append(self.create_formula(f"forecast{i}pop",'none','none')) + + for i in range(int(max(user_input[CONF_MAX_DAYS], user_input[CONF_INTIAL_DAYS]))): + if 'hist_rain' in options: + resources.append(self.create_formula(f"day{i}rain",'precipitation','measurement')) + if 'hist_snow' in options: + resources.append(self.create_formula(f"fday{i}snow",'precipitation','measurement')) + if 'hist_max' in options: + resources.append(self.create_formula(f"day{i}max",'temperature','measurement')) + if 'hist_min' in options: + resources.append(self.create_formula(f"day{i}min",'temperature','measurement')) + + if 'current_obs' in options: + resources.append(self.create_formula("current_rain",'precipitation','measurement')) + resources.append(self.create_formula("current_snow", 'precipitation','measurement')) + resources.append(self.create_formula("current_humidity", 'humidity','measurement')) + resources.append(self.create_formula("current_temp", 'temperature','measurement')) + resources.append(self.create_formula("current_pressure", 'pressure','measurement')) + + self._data[CONF_RESOURCES]=resources + #self._data.pop('create_sensors') - self._data.update({CONF_RESOURCES:[]}) - return await self.async_step_add() + return await self.async_step_menu() if user_input is None: default_input = {} else: default_input = user_input + schema = vol.Schema( { vol.Required(CONF_NAME,default=default_input.get(CONF_NAME,DEFAULT_NAME)): cv.string, @@ -114,14 +162,64 @@ async def async_step_user( vol.Required(CONF_MAX_DAYS,default=default_input.get(CONF_MAX_DAYS,5)): sel.NumberSelector({"min":1,"max":30}), vol.Required(CONF_INTIAL_DAYS,default=default_input.get(CONF_INTIAL_DAYS,5)): sel.NumberSelector({"min":1,"max":5}), vol.Required(CONF_MAX_CALLS,default=default_input.get(CONF_MAX_CALLS,500)): sel.NumberSelector({"min":500,"max":5000,"step":500}), + vol.Required('create_sensors'): sel.SelectSelector({"options": + ['hist_rain','hist_snow', 'hist_max', + 'hist_min', 'current_obs', + 'forecast_rain', 'forecast_snow','forecast_max', + 'forecast_min', 'forecast_humidity','forecast_pop'] + ,"multiple":True + }), } ) return self.async_show_form( step_id="user", data_schema=schema, errors=errors ) + async def async_step_update(self, user_input=None): + """Invoke when a user initiates a flow via the user interface.""" + errors = {} + newdata = {} + newdata.update(self._data) + if user_input is not None: + lat = self._data[CONF_LOCATION][CONF_LATITUDE] + lon = self._data[CONF_LOCATION][CONF_LONGITUDE] + api_key = user_input.get(CONF_API_KEY) + try: + api_online = await _is_owm_api_online(self.hass, api_key, lat, lon) + if not api_online: + errors[CONF_API_KEY] = "invalid_api_key" + except UnauthorizedError: + errors[CONF_API_KEY] = "invalid_api_key" + except APIRequestError: + errors[CONF_API_KEY] = "cannot_connect" + user_input[CONF_MAX_DAYS] = max(user_input[CONF_MAX_DAYS], user_input[CONF_INTIAL_DAYS]) + + if not errors: + newdata[CONF_NAME] = self._data.get(CONF_NAME) + newdata[CONF_API_KEY] = user_input.get(CONF_API_KEY) + newdata[CONF_LOCATION] = self._data.get(CONF_LOCATION) + newdata[CONF_MAX_DAYS] = int(user_input.get(CONF_MAX_DAYS)) + newdata[CONF_INTIAL_DAYS] = int(user_input.get(CONF_INTIAL_DAYS)) + newdata[CONF_MAX_CALLS] = int(user_input.get(CONF_MAX_CALLS)) + # Return the form of the next step. + self._data = newdata + return await self.async_step_init() + + schema = vol.Schema( + { + vol.Required(CONF_API_KEY, default=self._data.get(CONF_API_KEY)): cv.string, + vol.Required(CONF_MAX_DAYS, default=self._data.get(CONF_MAX_DAYS)): sel.NumberSelector({"min":1,"max":30}), + vol.Required(CONF_INTIAL_DAYS, default=self._data.get(CONF_INTIAL_DAYS)): sel.NumberSelector({"min":1,"max":30}), + vol.Required(CONF_MAX_CALLS, default=self._data.get(CONF_MAX_CALLS,1000)): sel.NumberSelector({"min":500,"max":5000,"step":500}), + } + ) + return self.async_show_form( + step_id="update", data_schema=schema, errors=errors + ) + + async def async_step_add(self, user_input=None): - '''Add sensor.''' + '''Add a sensor.''' errors = {} newdata = {} measurement = False @@ -144,23 +242,22 @@ async def async_step_add(self, user_input=None): errors[CONF_FORMULA] = "formula_variable" if not errors: - if user_input == {}: - #not data input return to the menu - return await self.async_step_menu() # Input is valid, set data. data = {} data[CONF_NAME] = user_input[CONF_NAME] data[CONF_FORMULA] = user_input[CONF_FORMULA] data[CONF_ATTRIBUTES] = user_input.get(CONF_ATTRIBUTES,None) - data[CONF_SENSORCLASS] = user_input.get(CONF_SENSORCLASS,'none') + data[CONF_SENSORCLASS] = user_input.get(CONF_SENSORCLASS,None) if measurement is True: data[CONF_STATECLASS] = 'measurement' if string is True: data[CONF_SENSORCLASS] = 'none' data[CONF_UID] = str(uuid.uuid4()) newdata[CONF_RESOURCES].append(data) + self._data = newdata return await self.async_step_menu() + if user_input is None: default_input = {} else: @@ -168,11 +265,11 @@ async def async_step_add(self, user_input=None): schema = vol.Schema( { vol.Required(CONF_NAME,default=default_input.get(CONF_NAME,'')): cv.string, - vol.Required(CONF_FORMULA,default=''): sel.TemplateSelector({}), + vol.Required(CONF_FORMULA,default=default_input.get(CONF_FORMULA,'')): sel.TemplateSelector({}), vol.Optional(CONF_ATTRIBUTES,default=default_input.get(CONF_ATTRIBUTES,'')): cv.string, vol.Required(CONF_SENSORCLASS,default=default_input.get(CONF_SENSORCLASS,'none')): sel.SelectSelector( sel.SelectSelectorConfig( - translation_key = "sensor_class", + translation_key="sensor_class", options=[ {"label":"None", "value":"none"}, {"label":"Humidity", "value":"humidity"}, @@ -203,14 +300,12 @@ async def async_step_delete(self, user_input=None): #no data provided return to the menu return await self.async_step_menu() - selected = int(user_input.get(CONF_NAME).split(".")[0]) - 1 + selected = int(user_input.get(CONF_NAME).split(".")[0]) newdata[CONF_RESOURCES].pop(selected) self._data = newdata return await self.async_step_menu() # build the list - #selection = 0 for selection, sensor in enumerate(self._data.get(CONF_RESOURCES)): - #selection += 1 sensors.append(str(selection) + '.' + sensor.get(CONF_NAME)) list_schema = vol.Schema({vol.Optional(CONF_NAME): vol.In(sensors)}) @@ -228,12 +323,10 @@ async def async_step_list_modify(self, user_input=None): #no data provided return to the menu return await self.async_step_menu() # Return the form of the next step. - self.selected = int(user_input.get(CONF_NAME).split(".")[0]) - 1 + self.selected = int(user_input.get(CONF_NAME).split(".")[0]) return await self.async_step_modify() #build the list for display - #selection = 0 for selection, sensor in enumerate(self._data.get(CONF_RESOURCES)): - #selection += 1 items.append(str(selection) + '.' + sensor.get(CONF_NAME)) list_schema = vol.Schema({vol.Optional(CONF_NAME): vol.In(items)}) @@ -309,7 +402,7 @@ async def async_step_modify(self, user_input=None): async def async_step_menu(self, user_input=None): '''Add a group or finalise the flow.''' - menu_options = ["list_modify"] + menu_options = ["add","list_modify"] if len(self._data.get(CONF_RESOURCES)) > 1: menu_options.extend(["delete"]) menu_options.extend(["finalise"]) @@ -399,8 +492,7 @@ async def async_step_update(self, user_input=None): errors[CONF_API_KEY] = "invalid_api_key" except APIRequestError: errors[CONF_API_KEY] = "cannot_connect" - if user_input[CONF_MAX_DAYS] < user_input[CONF_INTIAL_DAYS]: - user_input[CONF_MAX_DAYS] = user_input[CONF_INTIAL_DAYS] + user_input[CONF_MAX_DAYS] = max(user_input[CONF_MAX_DAYS], user_input[CONF_INTIAL_DAYS]) if not errors: newdata[CONF_NAME] = self._data.get(CONF_NAME) @@ -426,25 +518,25 @@ async def async_step_update(self, user_input=None): ) async def async_step_delete(self, user_input=None): - '''Delete.''' + '''List for Delete.''' errors = {} sensors = [] newdata = {} newdata.update(self._data) - + _LOGGER.error(newdata) if user_input is not None: if user_input == {}: #no data provided return to the menu return await self.async_step_init() - selected = int(user_input.get(CONF_NAME).split(".")[0]) - 1 + selected = int(user_input.get(CONF_NAME).split(".")[0]) + newdata[CONF_RESOURCES].pop(selected) self._data = newdata return await self.async_step_init() # build the list #selection = 0 for selection,sensor in enumerate(self._data.get(CONF_RESOURCES)): - #selection += 1 sensors.append(str(selection) + '.' + sensor.get(CONF_NAME)) list_schema = vol.Schema({vol.Optional(CONF_NAME): vol.In(sensors)}) @@ -453,7 +545,7 @@ async def async_step_delete(self, user_input=None): ) async def async_step_list_modify(self, user_input=None): - '''Update zone.''' + '''List Update zone.''' errors = {} items = [] @@ -462,12 +554,13 @@ async def async_step_list_modify(self, user_input=None): #no data provided return to the menu return await self.async_step_init() # Return the form of the next step. - self.selected = int(user_input.get(CONF_NAME).split(".")[0]) - 1 + self.selected = int(user_input.get(CONF_NAME).split(".")[0]) + return await self.async_step_modify() #build the list for display #selection = 0 for selection,sensor in enumerate(self._data.get(CONF_RESOURCES)): - #selection += 1 + items.append(str(selection) + '.' + sensor.get(CONF_NAME)) list_schema = vol.Schema({vol.Optional(CONF_NAME): vol.In(items)}) @@ -484,6 +577,7 @@ async def async_step_modify(self, user_input=None): newdata.update(self._data) this_sensor = newdata.get(CONF_RESOURCES)[self.selected] + if user_input is not None: evalform = evaluate_custom_formula(user_input.get(CONF_FORMULA),self._data.get(CONF_MAX_DAYS,0)) match evalform: @@ -594,12 +688,12 @@ async def async_step_add(self, user_input=None): sel.SelectSelectorConfig( translation_key="sensor_class", options=[ - {"label":"None", "value":"NONE"}, - {"label":"Humidity", "value":"HUMIDITY"}, - {"label":"Precipitation", "value":"PRECIPITATION"}, - {"label":"Precipitation Intensity", "value":"PRECIPITATIONINTENSITY"}, - {"label":"Temperature", "value":"TEMPERATURE"}, - {"label":"Pressure", "value":"PRESSURE"} + {"label":"None", "value":"none"}, + {"label":"Humidity", "value":"humidity"}, + {"label":"Precipitation", "value":"precipitation"}, + {"label":"Precipitation Intensity", "value":"precipitation_intensity"}, + {"label":"Temperature", "value":"temperature"}, + {"label":"Pressure", "value":"pressure"} ] )), } diff --git a/custom_components/openweathermaphistory/manifest.json b/custom_components/openweathermaphistory/manifest.json index b82a924..3aa0ffe 100644 --- a/custom_components/openweathermaphistory/manifest.json +++ b/custom_components/openweathermaphistory/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/petergridge/openweathermaphistory/issues", "requirements": ["pyowm==3.2.0"], - "version": "2.0.15" + "version": "2024.10.01" } \ No newline at end of file