diff --git a/custom_components/nordpool_planner/__init__.py b/custom_components/nordpool_planner/__init__.py index 247353e..7dfa642 100644 --- a/custom_components/nordpool_planner/__init__.py +++ b/custom_components/nordpool_planner/__init__.py @@ -33,16 +33,6 @@ PLATFORMS = [Platform.BINARY_SENSOR, Platform.NUMBER] -# async def async_setup(hass: HomeAssistant, config: Config) -> bool: -# hass.data.setdefault(DOMAIN, {}) -# return True - - -# async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: -# entry_data = dict(entry.data) -# hass.data[DOMAIN][entry.entry_id] = entry_data -# await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) -# return True async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up this integration using UI.""" config_entry.async_on_unload(config_entry.add_update_listener(async_reload_entry)) @@ -52,9 +42,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b if config_entry.entry_id not in hass.data[DOMAIN]: hass.data[DOMAIN][config_entry.entry_id] = NordpoolPlanner(hass, config_entry) - # else: - # planner = hass.data[DOMAIN][config_entry.entry_id] - # await planner.async_config_entry_first_refresh() if config_entry is not None: if config_entry.source == SOURCE_IMPORT: @@ -67,16 +54,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b return True -# async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: -# unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) -# if unload_ok: -# hass.data[DOMAIN].pop(entry.entry_id) -# return unload_ok async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unloading a config_flow entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: - hass.data[DOMAIN].pop(entry.entry_id) + planner = hass.data[DOMAIN].pop(entry.entry_id) + planner.cleanup() return unload_ok @@ -93,25 +76,17 @@ def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: """Initialize my coordinator.""" self._hass = hass self._config = config_entry + self._state_change_listners = [] - # if self._np_entity.unique_id is not None: - # self.async_on_remove( - # async_track_state_change_event( - # self._hass, - # [self._np_entity.unique_id], - # self._async_input_changed, - # ) - # ) - - # Internal states + # Input entities self._np_entity = NordpoolEntity(self._config.data[CONF_NP_ENTITY]) - - # # TODO: Dont seem to work as expected! - # async_track_state_change_event( - # self._hass, - # [self._np_entity.unique_id], - # self._async_input_changed, - # ) + self._state_change_listners.append( + async_track_state_change_event( + self._hass, + [self._np_entity.unique_id], + self._async_input_changed, + ) + ) # Configuration entities self._duration_number_entity = "" @@ -172,6 +147,11 @@ def _accept_rate(self) -> float: """Get accept rate parameter.""" return self.get_number_entity_value(self._accept_rate_number_entity) + def cleanup(self): + """Clenaup by removing event listners.""" + for lister in self._state_change_listners: + lister() + def get_number_entity_value( self, entity_id: str, integer: bool = False ) -> float | int | None: @@ -219,11 +199,12 @@ def register_input_entity_id(self, entity_id, conf_key) -> None: entity_id, conf_key, ) - # TODO: Dont seem to work as expected! - async_track_state_change_event( - self._hass, - [entity_id], - self._async_input_changed, + self._state_change_listners.append( + async_track_state_change_event( + self._hass, + [entity_id], + self._async_input_changed, + ) ) def register_output_listner_entity(self, entity, conf_key="") -> None: diff --git a/custom_components/nordpool_planner/number.py b/custom_components/nordpool_planner/number.py index 7a258c1..a1f43b3 100644 --- a/custom_components/nordpool_planner/number.py +++ b/custom_components/nordpool_planner/number.py @@ -52,7 +52,7 @@ key=CONF_SEARCH_LENGTH_ENTITY, device_class=NumberDeviceClass.DURATION, native_min_value=3, - native_max_value=12, + native_max_value=23, # Let's keep it below 24h to not risk wrapping a day. native_step=1, native_unit_of_measurement=UnitOfTime.HOURS, ) @@ -78,7 +78,7 @@ async def async_setup_entry( entities.append( NordpoolPlannerNumber( planner, - callback=planner.input_changed, + # callback=planner.input_changed, start_val=3, entity_description=DURATION_ENTITY_DESCRIPTION, ) @@ -86,7 +86,7 @@ async def async_setup_entry( if config_entry.data.get(CONF_ACCEPT_COST_ENTITY): entity_description = ACCEPT_COST_ENTITY_DESCRIPTION - # Override if curensy option is set + # Override if currency option is set if currency := config_entry.options.get(CONF_CURENCY): entity_description = NumberEntityDescription( key=ACCEPT_COST_ENTITY_DESCRIPTION.key, @@ -99,7 +99,7 @@ async def async_setup_entry( entities.append( NordpoolPlannerNumber( planner, - callback=planner.input_changed, + # callback=planner.input_changed, start_val=0.0, entity_description=entity_description, ) @@ -109,7 +109,7 @@ async def async_setup_entry( entities.append( NordpoolPlannerNumber( planner, - callback=planner.input_changed, + # callback=planner.input_changed, start_val=0.1, entity_description=ACCEPT_RATE_ENTITY_DESCRIPTION, ) @@ -119,7 +119,7 @@ async def async_setup_entry( entities.append( NordpoolPlannerNumber( planner, - callback=planner.input_changed, + # callback=planner.input_changed, start_val=10, entity_description=SEARCH_LENGTH_ENTITY_DESCRIPTION, ) @@ -129,7 +129,7 @@ async def async_setup_entry( entities.append( NordpoolPlannerNumber( planner, - callback=planner.input_changed, + # callback=planner.input_changed, start_val=7, entity_description=END_TIME_ENTITY_DESCRIPTION, ) @@ -145,7 +145,7 @@ class NordpoolPlannerNumber(NordpoolPlannerEntity, RestoreNumber): def __init__( self, planner, - callback, + # callback, start_val, entity_description: NumberEntityDescription, ) -> None: @@ -153,7 +153,7 @@ def __init__( super().__init__(planner) self.entity_description = entity_description self._default_value = start_val - self._callback = callback + # self._callback = callback self._attr_name = ( self._planner.name + " " @@ -188,5 +188,5 @@ async def async_set_native_value(self, value: float) -> None: value, self.name, ) - self._callback(value) + # self._callback(value) self.async_schedule_update_ha_state() diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..a5e9c24 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,64 @@ +[coverage:run] +source = + custom_components + +[coverage:report] +exclude_lines = + pragma: no cover + raise NotImplemented() + if __name__ == '__main__': + main() +show_missing = true + +[tool:pytest] +testpaths = tests +norecursedirs = .git +asyncio_mode = auto +addopts = + -p syrupy + --strict + --cov=custom_components + +[flake8] +# https://github.com/ambv/black#line-length +max-line-length = 88 +# E501: line too long +# W503: Line break occurred before a binary operator +# E203: Whitespace before ':' +# D202 No blank lines allowed after function docstring +# W504 line break after binary operator +ignore = + E501, + W503, + E203, + D202, + W504 + +[isort] +# https://github.com/timothycrosley/isort +# https://github.com/timothycrosley/isort/wiki/isort-Settings +# splits long import on multiple lines indented by 4 spaces +multi_line_output = 3 +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True +line_length=88 +indent = " " +# by default isort don't check module indexes +not_skip = __init__.py +# will group `import x` and `from x import` of the same module. +force_sort_within_sections = true +sections = FUTURE,STDLIB,INBETWEENS,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +default_section = THIRDPARTY +known_first_party = custom_components,tests +forced_separate = tests +combine_as_imports = true + +[mypy] +python_version = 3.7 +ignore_errors = true +follow_imports = silent +ignore_missing_imports = true +warn_incomplete_stub = true +warn_redundant_casts = true +warn_unused_configs = true \ No newline at end of file diff --git a/tests/bandit.yaml b/tests/bandit.yaml new file mode 100644 index 0000000..1b5bc66 --- /dev/null +++ b/tests/bandit.yaml @@ -0,0 +1,17 @@ +# https://bandit.readthedocs.io/en/latest/config.html + +tests: + - B108 + - B306 + - B307 + - B313 + - B314 + - B315 + - B316 + - B317 + - B318 + - B319 + - B320 + - B325 + - B602 + - B604 \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..b3def45 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,9 @@ +"""Fixtures for testing.""" + +import pytest + + +@pytest.fixture(autouse=True) +def auto_enable_custom_integrations(enable_custom_integrations): + """Enable custom integrations.""" + return diff --git a/tests/test_planner.py b/tests/test_planner.py index f00cf17..d993ee8 100644 --- a/tests/test_planner.py +++ b/tests/test_planner.py @@ -15,22 +15,40 @@ from custom_components.nordpool_planner.const import * from homeassistant import config_entries -from homeassistant.components import sensor -from homeassistant.core import HomeAssistant + +# from homeassistant.components import sensor +# from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback +NAME = "My planner 1" +TYPE = "moving" +DURATION_ENT = "duration_ent" +SEARCH_LENGTH_ENT = "search_len" +NP_ENT = "sensor.np_ent" +CURRENCY = "EUR" + +CONF_ENTRY = config_entries.ConfigEntry( + data={ + CONF_NAME: NAME, + CONF_TYPE: TYPE, + CONF_NP_ENTITY: NP_ENT, + CONF_DURATION_ENTITY: DURATION_ENT, + CONF_SEARCH_LENGTH_ENTITY: SEARCH_LENGTH_ENT, + }, + options={CONF_CURENCY: CURRENCY}, + domain=DOMAIN, + version=1, + minor_version=2, + source="user", + title="Nordpool Planner", + unique_id="123456", +) + @pytest.mark.asyncio async def test_planner_init(hass): """Test the planner initialization.""" - NAME = "My planner 1" - TYPE = "moving" - DURATION_ENT = "duration_ent" - SEARCH_LENGTH_ENT = "search_len" - NP_ENT = "sensor.np_ent" - CURRENCY = "EUR" - # async def async_setup_entry_init( # hass: HomeAssistant, config_entry: config_entries.ConfigEntry # ) -> bool: @@ -48,23 +66,6 @@ async def test_planner_init(hass): # ), # ) - config_entry = config_entries.ConfigEntry( - data={ - CONF_NAME: NAME, - CONF_TYPE: TYPE, - CONF_NP_ENTITY: NP_ENT, - CONF_DURATION_ENTITY: DURATION_ENT, - CONF_SEARCH_LENGTH_ENTITY: SEARCH_LENGTH_ENT, - }, - options={CONF_CURENCY: CURRENCY}, - domain=DOMAIN, - version=1, - minor_version=2, - source="user", - title="Nordpool Planner", - unique_id="123456", - ) - # # Fake nordpool sensor # np_sensor = sensor.SensorEntity() # np_sensor.entity_id = NP_ENT @@ -84,7 +85,7 @@ async def test_planner_init(hass): # MockPlatform(async_setup_entry=async_setup_entry_platform), # ) - planner = NordpoolPlanner(hass, config_entry) + planner = NordpoolPlanner(hass, CONF_ENTRY) assert planner.name == NAME assert planner._is_static == False