Skip to content

Commit

Permalink
V2024.10.01
Browse files Browse the repository at this point in the history
- Fix indexing of Update and delete lists in config flow
- Auto create sensors on initial creation of the component. (on update is next)
  • Loading branch information
petergridge authored Oct 7, 2024
1 parent c6af14a commit f661610
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 36 deletions.
164 changes: 129 additions & 35 deletions custom_components/openweathermaphistory/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand All @@ -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]
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -144,35 +242,34 @@ 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:
default_input = user_input
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"},
Expand Down Expand Up @@ -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)})

Expand All @@ -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)})

Expand Down Expand Up @@ -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"])
Expand Down Expand Up @@ -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)
Expand All @@ -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)})

Expand All @@ -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 = []

Expand All @@ -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)})

Expand All @@ -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:
Expand Down Expand Up @@ -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"}
]
)),
}
Expand Down
2 changes: 1 addition & 1 deletion custom_components/openweathermaphistory/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

0 comments on commit f661610

Please sign in to comment.