-
Notifications
You must be signed in to change notification settings - Fork 3
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
Heating scheduler #37
Merged
Merged
Changes from 10 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
42c7905
Made example.py run without errors
firstdayofjune 68a395e
Added black & isort
firstdayofjune b596c34
Added black & isort
firstdayofjune 8d9f8d4
Minor refactoring to client
firstdayofjune d1825b2
Fix isort in pre-commit
firstdayofjune bc86f3f
Added a Heating Scheduler
firstdayofjune 4fb95e2
Merge remote-tracking branch 'origin/master' into heating_scheduler
firstdayofjune 9576e6d
Updated lockfile
firstdayofjune 3ae2b81
Removed isort duplicate from pipfile
firstdayofjune ea8f54c
Fixed Code Quality issues
firstdayofjune f879943
Minor refactorings after Code Review
firstdayofjune e97ac8e
Merge remote-tracking branch 'origin/master' into heating_scheduler
firstdayofjune 67ee878
Removed fixed python version from pipfile, as it is not available on CI.
firstdayofjune 88995e5
Merge branch 'master' into heating_scheduler
inverse b6abf52
Merge branch 'master' into heating_scheduler
inverse File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,3 +18,6 @@ __pycache__ | |
# Coverage | ||
/coverage.xml | ||
/.coverage | ||
|
||
# Jupyter | ||
.ipynb_checkpoints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
class IOLiteError(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
from base64 import b64encode | ||
from enum import Enum | ||
from typing import Tuple | ||
|
||
import requests | ||
from iolite.exceptions import IOLiteError | ||
|
||
|
||
class Day(Enum): | ||
"""Day constants for heating intervals. | ||
|
||
The heating interval API does not have the concept of days. Instead, an interval starting at 0 is considered Monday | ||
morning. To set the same interval on Tuesday, a 24 hour offset has to be set (in minutes). | ||
""" | ||
|
||
MONDAY = 60 * 24 * 0 | ||
TUESDAY = 60 * 24 * 1 | ||
WEDNESDAY = 60 * 24 * 2 | ||
THURSDAY = 60 * 24 * 3 | ||
FRIDAY = 60 * 24 * 4 | ||
SATURDAY = 60 * 24 * 5 | ||
SUNDAY = 60 * 24 * 6 | ||
|
||
|
||
class HeatingSchedulerError(IOLiteError): | ||
pass | ||
|
||
|
||
class HeatingScheduler(object): | ||
BASE_URL = "https://remote.iolite.de" | ||
HEATING_ENDPOINT = "/heating/api/heating/" | ||
|
||
def __init__(self, sid: str, username: str, password: str, room_id: str): | ||
"""The HeatingScheduler comprises methods to interact with the heating interval API. | ||
|
||
:param sid: The session ID, used for authentication | ||
:param username: The username, used for authentication | ||
:param password: The password mathing the username, used for authentication | ||
:param room_id: The room to set or change the heating intervals for. | ||
""" | ||
self.sid = sid | ||
self.username = username | ||
self.password = password | ||
self.room_id = room_id | ||
user_pass = f"{self.username}:{self.password}" | ||
self.auth_value = b64encode(user_pass.encode()).decode("ascii") | ||
|
||
def _prepare_request_arguments(self) -> Tuple[str, dict]: | ||
url = f"{self.BASE_URL}{self.HEATING_ENDPOINT}{self.room_id}" | ||
headers = {"Authorization": f"Basic {self.auth_value}"} | ||
params = {"SID": self.sid} | ||
return ( | ||
url, | ||
{ | ||
"headers": headers, | ||
"params": params, | ||
}, | ||
) | ||
|
||
def set_comfort_temperature(self, temperature: float) -> requests.Response: | ||
"""Sets the desired comfort temperature for all heating intervals. | ||
|
||
:param temperature: The temperature in degrees celcius | ||
:return: The API response | ||
""" | ||
temperature_within_range = 14 <= temperature <= 30 | ||
firstdayofjune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not (temperature_within_range): | ||
firstdayofjune marked this conversation as resolved.
Show resolved
Hide resolved
|
||
raise HeatingSchedulerError( | ||
"The desired comfort temperature has to be between 14 and 30 degrees celsius." | ||
) | ||
url, params = self._prepare_request_arguments() | ||
response = requests.put(url, json={"comfortTemperature": temperature}, **params) | ||
return response | ||
|
||
def add_interval( | ||
self, day: Day, hour: int, minute: int, duration: int | ||
) -> requests.Response: | ||
"""Schedules a heating interval | ||
|
||
:param day: The day to set the interval for | ||
:param hour: The hour to begin the interval at | ||
:param minute: The minute of the hour to begin the interval at | ||
:param duration: The duration of for the interval to last in minutes | ||
:return: The API response, including the new interval's iolite ID | ||
""" | ||
url, params = self._prepare_request_arguments() | ||
response = requests.post( | ||
url + "/intervals", | ||
json={ | ||
"startTimeInMinutes": day.value + hour * 60 + minute, | ||
"durationInMinutes": duration, | ||
}, | ||
**params, | ||
) | ||
return response | ||
|
||
def delete_interval(self, interval_id: str) -> requests.Response: | ||
"""Deletes the given interval | ||
|
||
:param interval_id: iolite ID of the interval | ||
:return: The API response | ||
""" | ||
url, params = self._prepare_request_arguments() | ||
response = requests.delete(url + f"/intervals/{interval_id}", **params) | ||
return response |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Why do we need to declare this?
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.
For me, the example.py threw an error because iolite was not an installed package. So I added the iolite directory as an editable dev-dependency. This way you can
import iolite
without having the package actually installed in the environment.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.
Ahh now I see it. It feels weird having the package add itself as a dependency but I am not sure of a better way.
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.
Another solution would be to turn
scripts
into a module (add__init__.py
) and then it can be invoked aspython -m scripts.example
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.
That would work for the scripts, yes. I just realized the
iolite = {editable = true,path = "."}
is also needed to import iolite inside the notebooks, though. So I would like to keep this change.