Skip to content

Commit

Permalink
restructure, format fixes, simplified hass import
Browse files Browse the repository at this point in the history
  • Loading branch information
benleb committed Oct 1, 2019
1 parent dfc29df commit 4758cf3
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 46 deletions.
93 changes: 48 additions & 45 deletions apps/notifreeze/notifreeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"""
import re
from datetime import datetime as dt
from typing import Any, Dict, Sequence, Union
import appdaemon.plugins.hass.hassapi as hass
from typing import Any, Dict, Sequence

from adutils import ADutils
import adutils
import hassapi as hass

APP_NAME = "NotiFreeze"
APP_ICON = "❄️ "
Expand All @@ -30,18 +30,16 @@ class NotiFreeze(hass.Hass): # type: ignore

def initialize(self) -> None:
"""Set up state listener."""
self.app_config: Dict[str, Union[int, float, str]] = dict()
self.app_config["notify_service"] = str(self.args.get("notify_service"))
self.cfg: Dict[str, Any] = dict()
self.cfg["notify_service"] = str(self.args.get("notify_service"))
self.sensor_outdoor = str(self.args.get("outdoor_temperature"))
# time until notifications are triggered
self.app_config["max_difference"] = float(self.args.get("max_difference", 5))
self.cfg["max_difference"] = float(self.args.get("max_difference", 5))
# times/durations are given in minutes
self.app_config["initial_delay"] = int(self.args.get("initial_delay", 5))
self.app_config["reminder_delay"] = int(self.args.get("reminder_delay", 3))
self.cfg["initial_delay"] = int(self.args.get("initial_delay", 5))
self.cfg["reminder_delay"] = int(self.args.get("reminder_delay", 3))

if self.app_config["notify_service"] and self.entity_exists(
self.sensor_outdoor
):
if self.cfg["notify_service"] and self.entity_exists(self.sensor_outdoor):

self.sensors: Dict[str, str] = dict()
self.handles: Dict[str, str] = dict()
Expand All @@ -55,8 +53,17 @@ def initialize(self) -> None:
self.sensors[entity] = f"sensor.temperature_{room}"
self.listen_state(self.handler, entity=entity)

self.adu = ADutils(APP_NAME, self.app_config, icon=APP_ICON, ad=self)
self.adu.show_info()
# set units
self.cfg.setdefault(
"_units",
dict(max_difference="°C", initial_delay="min", reminder_delay="min"),
)
self.cfg.setdefault("_prefixes", dict(max_difference="±"))

# init adutils
self.adu = adutils.ADutils(
APP_NAME, self.cfg, icon=APP_ICON, ad=self, show_config=True
)

def handler(
self, entity: str, attr: Any, old: str, new: str, kwargs: Dict[str, Any]
Expand All @@ -65,25 +72,30 @@ def handler(
try:
indoor, outdoor, difference = self.get_temperatures(entity)
except (ValueError, TypeError) as error:
self.error(f"No valid temperature values from sensor {entity}: {error}")
self.adu.log(
f"No valid temperature values from sensor {entity}: {error}",
icon=APP_ICON,
level="ERROR",
)
return

if (
old == "off"
and new == "on"
and difference > float(self.app_config["max_difference"])
and abs(difference) > float(self.cfg["max_difference"])
):

# door/window opened, schedule reminder/notification
self.handles[entity] = self.run_in(
self.notification,
self.app_config["initial_delay"] * self.SECONDS_PER_MIN,
self.cfg["initial_delay"] * self.SECONDS_PER_MIN,
entity_id=entity,
)

self.adu.log(
f"reminder: ({self.app_config['initial_delay']}min): "
f"{self.strip_sensor(entity)} | diff: {difference:.1f}°C",
f"\033[1m{self.friendly_name(entity)}\033[0m opened, "
f"\033[1m{difference:+.1f}°C\033[0m → "
f"reminder in \033[1m{self.cfg['initial_delay']}min\033[0m",
icon=APP_ICON,
)

Expand All @@ -100,13 +112,15 @@ def notification(self, kwargs: Dict[str, Any]) -> None:
indoor, outdoor, difference = self.get_temperatures(entity)
except (ValueError, TypeError) as error:
self.adu.log(
f"No valid temperature values to calculate difference: {error}"
f"No valid temperature values to calculate difference: {error}",
icon=APP_ICON,
level="ERROR",
)
return

# check if all required conditions still met, then processing with notification
if (
difference > float(self.app_config["max_difference"])
abs(difference) > float(self.cfg["max_difference"])
and self.get_state(entity) == "on"
and entity != "binary_sensor.door_window_sensor_basement_window"
):
Expand All @@ -128,28 +142,27 @@ def notification(self, kwargs: Dict[str, Any]) -> None:
# build notification/log message
message = (
f"{self.friendly_name(entity)} seit {open_since} offen!\n"
f"{self.friendly_name(self.sensors[entity])}: {indoor:.1f}°C | "
f"Aussen: {outdoor:.1f}°C"
# f"\n{counter} {last_changed}"
f"\033[1m{difference:+.1f}°C\033[0m → "
f"{self.friendly_name(self.sensors[entity])}: {indoor:.1f}°C"
)

# send notification
self.call_service(
str(self.app_config["notify_service"]).replace(".", "/"),
message=message,
str(self.cfg["notify_service"]).replace(".", "/"),
message=re.sub(r"\033\[\dm", "", message),
)

# schedule next reminder
self.handles[entity] = self.run_in(
self.notification,
self.app_config["reminder_delay"] * self.SECONDS_PER_MIN,
self.cfg["reminder_delay"] * self.SECONDS_PER_MIN,
entity_id=entity,
counter=counter + 1,
)

# debug
self.adu.log(
f"notification sent to {self.app_config['notify_service']}: "
f"{message}",
f"notification sent to {self.cfg['notify_service']}: " f"{message}",
icon=APP_ICON,
level="DEBUG",
)
Expand All @@ -160,25 +173,15 @@ def notification(self, kwargs: Dict[str, Any]) -> None:

def kill_timer(self, entity: str) -> None:
"""Cancel scheduled task/timers."""
self.cancel_timer(self.handles[entity])
self.adu.log(f"reminder deleted: {self.strip_sensor(entity)}", icon=APP_ICON)
if self.handles[entity]:
self.cancel_timer(self.handles[entity])
self.adu.log(
f"\033[1m{self.friendly_name(entity)}\033[0m closed → timer stopped",
icon=APP_ICON,
)

def get_temperatures(self, entity: str) -> Sequence[float]:
"""Get temperature indoor, outdoor and the abs. difference of both."""
indoor = float(self.get_state(self.sensors[entity], default=STATE_UNKNOWN))
outdoor = float(self.get_state(self.sensor_outdoor, default=STATE_UNKNOWN))
return (indoor, outdoor, abs(indoor - outdoor))
# indoor_state = self.get_state(self.sensors[entity], default=STATE_UNKNOWN)
# outdoor_state = self.get_state(self.sensor_outdoor, default=STATE_UNKNOWN)
# if STATE_UNKNOWN not in (indoor_state, outdoor_state):
# indoor_temperature = float(indoor_state)
# outdoor_temperature = float(outdoor_state)
# absolute_difference = abs(indoor_temperature - outdoor_temperature)
# return (indoor_temperature, outdoor_temperature, absolute_difference)
# else:
# raise ValueError(
# f"Unknown state! indoor: {indoor_state}, outdoor: {outdoor_state}"
# )

def strip_sensor(self, sensor: str) -> str:
return str(self.split_entity(sensor)[1].replace("door_window_sensor_", ""))
return (indoor, outdoor, outdoor - indoor)
2 changes: 1 addition & 1 deletion apps/notifreeze/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
adutils~=0.4.0
adutils~=0.4.1

0 comments on commit 4758cf3

Please sign in to comment.