pax_global_header00006660000000000000000000000064142007641420014512gustar00rootroot0000000000000052 comment=38e0018ac1dc84603d20437875f4c4f1db06d9a0 timmo001-ovoenergy-38e0018/000077500000000000000000000000001420076414200153455ustar00rootroot00000000000000timmo001-ovoenergy-38e0018/.github/000077500000000000000000000000001420076414200167055ustar00rootroot00000000000000timmo001-ovoenergy-38e0018/.github/CODEOWNERS000066400000000000000000000000241420076414200202740ustar00rootroot00000000000000.github/* @timmo001 timmo001-ovoenergy-38e0018/.github/CODE_OF_CONDUCT.md000066400000000000000000000066601420076414200215140ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [contact@timmo.xyz](contact@timmo.xyz). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) timmo001-ovoenergy-38e0018/.github/CONTRIBUTING.md000066400000000000000000000011501420076414200211330ustar00rootroot00000000000000# Contributing You are welcome to edit, fork and create pull requests as you like. - If you make a change, make sure to refer to any issues that it fixes. - Make sure you have used linters and tested what you have added/changed works. - Any pull request will be verified by the bots, so make sure you fix any issues the bots find. - I, and any other users I approve will be able to merge your changes so be patient. ## Docs Feel free to improve documentation as you like by making a pull request in `/docs`. Once merged into the master branch, my ci server will update the docs in the `gh-pages` branch. timmo001-ovoenergy-38e0018/.github/FUNDING.yml000066400000000000000000000001241420076414200205170ustar00rootroot00000000000000--- github: timmo001 ko_fi: timmo001 custom: - https://www.buymeacoffee.com/timmo timmo001-ovoenergy-38e0018/.github/ISSUE_TEMPLATE/000077500000000000000000000000001420076414200210705ustar00rootroot00000000000000timmo001-ovoenergy-38e0018/.github/ISSUE_TEMPLATE/bug.md000066400000000000000000000004051420076414200221660ustar00rootroot00000000000000--- name: 'Bug' about: A bug found the application. This is not a support question. Please ask these via the support channels. --- # Description ## How to reproduce the issue (if applicable) ## Screenshots (if applicable) ## Additional information timmo001-ovoenergy-38e0018/.github/ISSUE_TEMPLATE/feature-request.md000066400000000000000000000002021420076414200245250ustar00rootroot00000000000000--- name: 'Feature request' about: Suggest an idea for this project --- # Description ## Suggested Actions ## Additional timmo001-ovoenergy-38e0018/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000011071420076414200225050ustar00rootroot00000000000000# Proposed Changes > (Describe the changes and rationale behind them) ## Related Issues > ([Github link][autolink-references] to related issues or pull requests) ## Checklist - [ ] Change has been tested and works on my device(s). [autolink-references]: https://help.github.com/articles/autolinked-references-and-urls/ timmo001-ovoenergy-38e0018/.github/autolabeler.yml000066400000000000000000000000541420076414200217260ustar00rootroot00000000000000--- "Type: Documentation": ["*.md", "*.j2"] timmo001-ovoenergy-38e0018/.github/dependabot.yaml000066400000000000000000000003561420076414200217020ustar00rootroot00000000000000--- version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: daily - package-ecosystem: "pip" directory: "/" open-pull-requests-limit: 20 schedule: interval: "daily" timmo001-ovoenergy-38e0018/.github/labels.yml000066400000000000000000000054251420076414200207000ustar00rootroot00000000000000--- - name: "breaking-change" color: ee0701 description: "A breaking change for existing users." - name: "bugfix" color: ee0701 description: "Inconsistencies or issues which will cause a problem for users or implementors." - name: "documentation" color: 0052cc description: "Solely about the documentation of the project." - name: "enhancement" color: 1d76db description: "Enhancement of the code, not introducing new features." - name: "refactor" color: 1d76db description: "Improvement of existing code, not introducing new features." - name: "performance" color: 1d76db description: "Improving performance, not introducing new features." - name: "new-feature" color: 0e8a16 description: "New features or options." - name: "maintenance" color: 2af79e description: "Generic maintenance tasks." - name: "ci" color: 1d76db description: "Work that improves the continue integration." - name: "dependencies" color: 1d76db description: "Upgrade or downgrade of project dependencies." - name: "in-progress" color: fbca04 description: "Issue is currently being resolved by a developer." - name: "stale" color: fef2c0 description: "There has not been activity on this issue or PR for quite some time." - name: "no-stale" color: fef2c0 description: "This issue or PR is exempted from the stable bot." - name: "security" color: ee0701 description: "Marks a security issue that needs to be resolved asap." - name: "incomplete" color: fef2c0 description: "Marks a PR or issue that is missing information." - name: "invalid" color: fef2c0 description: "Marks a PR or issue that is missing information." - name: "beginner-friendly" color: 0e8a16 description: "Good first issue for people wanting to contribute to the project." - name: "help-wanted" color: 0e8a16 description: "We need some extra helping hands or expertise in order to resolve this." - name: "hacktoberfest" description: "Issues/PRs are participating in the Hacktoberfest." color: fbca04 - name: "hacktoberfest-accepted" description: "Issues/PRs are participating in the Hacktoberfest." color: fbca04 - name: "priority-critical" color: ee0701 description: "This should be dealt with ASAP. Not fixing this issue would be a serious error." - name: "priority-high" color: b60205 description: "After critical issues are fixed, these should be dealt with before any further issues." - name: "priority-medium" color: 0e8a16 description: "This issue may be useful, and needs some attention." - name: "priority-low" color: e4ea8a description: "Nice addition, maybe... someday..." - name: "major" color: b60205 description: "This PR causes a major version bump in the version number." - name: "minor" color: 0e8a16 description: "This PR causes a minor version bump in the version number." timmo001-ovoenergy-38e0018/.github/release-drafter.yml000066400000000000000000000020471420076414200225000ustar00rootroot00000000000000--- name-template: "v$RESOLVED_VERSION" tag-template: "v$RESOLVED_VERSION" change-template: "- $TITLE @$AUTHOR (#$NUMBER)" sort-direction: ascending categories: - title: "🚨 Breaking changes" labels: - "breaking-change" - title: "✨ New features" labels: - "new-feature" - title: "πŸ› Bug fixes" labels: - "bugfix" - title: "πŸš€ Enhancements" labels: - "enhancement" - "refactor" - "performance" - title: "🧰 Maintenance" labels: - "maintenance" - "ci" - title: "πŸ“š Documentation" labels: - "documentation" - title: "⬆️ Dependency updates" labels: - "dependencies" version-resolver: major: labels: - "major" - "breaking-change" minor: labels: - "minor" - "new-feature" patch: labels: - "bugfix" - "chore" - "ci" - "dependencies" - "documentation" - "enhancement" - "performance" - "refactor" default: patch template: | ## What’s changed $CHANGES timmo001-ovoenergy-38e0018/.github/workflows/000077500000000000000000000000001420076414200207425ustar00rootroot00000000000000timmo001-ovoenergy-38e0018/.github/workflows/ci.yml000066400000000000000000000053131420076414200220620ustar00rootroot00000000000000name: CI # yamllint disable-line rule:truthy on: push: pull_request: types: - opened - reopened - synchronize workflow_dispatch: env: DEFAULT_PYTHON: 3.9 jobs: lint-black: name: πŸ‘• Run Black runs-on: ubuntu-latest steps: - name: ‡️ Check out code from GitHub uses: actions/checkout@v2.4.0 - name: πŸ— Set up Python ${{ env.DEFAULT_PYTHON }} uses: actions/setup-python@v2.3.2 id: python with: python-version: ${{ env.DEFAULT_PYTHON }} - name: πŸš€ Run black run: | pip install -U black black --check . lint-jsonlint: name: πŸ‘• JSONLint runs-on: ubuntu-latest steps: - name: ‡️ Check out code from GitHub uses: actions/checkout@v2.4.0 - name: πŸš€ Run JSONLint run: | sudo apt install -y jsonlint for file in $(find ./ -type f -name "*.json"); do if ! jsonlint-php -q $file; then export FAILED=1 else echo "$file OK" fi done if [ "${FAILED}" = "1" ]; then exit 1 fi lint-markdownlint: name: πŸ‘• MarkdownLint runs-on: ubuntu-latest steps: - name: ‡️ Check out code from GitHub uses: actions/checkout@v2.4.0 - name: πŸš€ Run mdl uses: actionshub/markdownlint@2.0.2 lint-markdown-links: name: πŸ‘• Markdown Link Check runs-on: ubuntu-latest steps: - name: ‡️ Check out code from GitHub uses: actions/checkout@v2.4.0 - name: πŸš€ Run Markdown Links uses: gaurav-nelson/github-action-markdown-link-check@1.0.13 with: use-quiet-mode: no use-verbose-mode: no config-file: mlc_config.json folder-path: .github file-path: ./README.md max-depth: -1 check-modified-files-only: no base-branch: master file-extension: .md lint-prettier: name: πŸ‘• Prettier runs-on: ubuntu-latest steps: - name: ‡️ Check out code from GitHub uses: actions/checkout@v2.4.0 - name: πŸš€ Run Prettier uses: creyD/prettier_action@v4.2 with: prettier_options: --write **/*.{js,json,ts,tsx,yml,yaml} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} lint-yamllint: name: πŸ‘• YAMLLint runs-on: ubuntu-latest steps: - name: ‡️ Check out code from GitHub uses: actions/checkout@v2.4.0 - name: πŸš€ Run YAMLLint uses: ibiqlik/action-yamllint@v3.1 with: file_or_dir: . config_file: .yamllint.yml format: colored strict: false timmo001-ovoenergy-38e0018/.github/workflows/codeql-analysis.yml000066400000000000000000000046221420076414200245610ustar00rootroot00000000000000name: "CodeQL" # yamllint disable-line rule:truthy on: push: branches: [master] pull_request: # The branches below must be a subset of the branches above branches: [master] schedule: - cron: "0 12 * * 4" jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] language: ["python"] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - name: Checkout repository uses: actions/checkout@v2.4.0 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 # If this run was triggered by a pull request event, then checkout # the head of the pull request instead of the merge commit. - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # πŸ“š https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 timmo001-ovoenergy-38e0018/.github/workflows/deploy.yml000066400000000000000000000014731420076414200227660ustar00rootroot00000000000000--- name: Deploy # yamllint disable-line rule:truthy on: release: types: - published workflow_run: workflows: ["CI"] branches: [master] types: - completed jobs: deploy: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: - uses: actions/checkout@v2.4.0 - name: Set up Python uses: actions/setup-python@v2.3.2 with: python-version: "3.x" - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python setup.py sdist bdist_wheel twine upload dist/* timmo001-ovoenergy-38e0018/.github/workflows/labels.yml000066400000000000000000000007131420076414200227300ustar00rootroot00000000000000--- name: Sync labels # yamllint disable-line rule:truthy on: push: branches: - master paths: - .github/labels.yml jobs: labels: name: ♻️ Sync labels runs-on: ubuntu-latest steps: - name: ‡️ Check out code from GitHub uses: actions/checkout@v2.4.0 - name: πŸš€ Run Label Syncer uses: micnncim/action-label-syncer@v1.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} timmo001-ovoenergy-38e0018/.github/workflows/lock.yml000066400000000000000000000006731420076414200224230ustar00rootroot00000000000000--- name: Lock # yamllint disable-line rule:truthy on: schedule: - cron: "0 9 * * *" workflow_dispatch: jobs: lock: name: πŸ”’ Lock closed issues and PRs runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v3 with: github-token: ${{ github.token }} issue-lock-inactive-days: "30" issue-lock-reason: "" pr-lock-inactive-days: "1" pr-lock-reason: "" timmo001-ovoenergy-38e0018/.github/workflows/release-drafter.yml000066400000000000000000000005521420076414200245340ustar00rootroot00000000000000--- name: Release Drafter # yamllint disable-line rule:truthy on: push: branches: - master jobs: update_release_draft: name: ✏️ Draft release runs-on: ubuntu-latest steps: - name: πŸš€ Run Release Drafter uses: release-drafter/release-drafter@v5.18.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} timmo001-ovoenergy-38e0018/.github/workflows/stale.yml000066400000000000000000000025541420076414200226030ustar00rootroot00000000000000--- name: Stale # yamllint disable-line rule:truthy on: schedule: - cron: "0 8 * * *" workflow_dispatch: jobs: stale: name: 🧹 Clean up stale issues and PRs runs-on: ubuntu-latest steps: - name: πŸš€ Run stale uses: actions/stale@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 days-before-close: 7 remove-stale-when-updated: true stale-issue-label: "stale" exempt-issue-labels: "no-stale,help-wanted" stale-issue-message: > There hasn't been any activity on this issue recently, so we clean up some of the older and inactive issues. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by leaving a comment πŸ‘ This issue has now been marked as stale and will be closed if no further activity occurs. Thanks! stale-pr-label: "stale" exempt-pr-labels: "no-stale" stale-pr-message: > There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days. Thank you for your contributions. timmo001-ovoenergy-38e0018/.gitignore000066400000000000000000000025101420076414200173330ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # Other .vscode/ *.egg* timmo001-ovoenergy-38e0018/.mdl.rb000066400000000000000000000001421420076414200165210ustar00rootroot00000000000000all rule 'MD013', :tables => false exclude_rule 'MD002' exclude_rule 'MD024' exclude_rule 'MD041' timmo001-ovoenergy-38e0018/.mdlrc000066400000000000000000000000201420076414200164370ustar00rootroot00000000000000style '.mdl.rb' timmo001-ovoenergy-38e0018/.yamllint.yml000066400000000000000000000001761420076414200200030ustar00rootroot00000000000000--- extends: default rules: line-length: ignore: | .gitlab-ci.yml .github/ .yarn/ level: warning timmo001-ovoenergy-38e0018/LICENSE.md000066400000000000000000000020571420076414200167550ustar00rootroot00000000000000# MIT License Copyright (c) 2020 Aidan Timson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. timmo001-ovoenergy-38e0018/README.md000066400000000000000000000020061420076414200166220ustar00rootroot00000000000000# ovoenergy [![GitHub Release][releases-shield]][releases] ![Project Stage][project-stage-shield] [![License][license-shield]](LICENSE.md) [![Buy me a coffee][buymeacoffee-shield]][buymeacoffee] Get energy data from OVO's API. ## Links [Contribution Guidelines][CONTRIBUTING] [Code of Conduct][CODE_OF_CONDUCT] [buymeacoffee-shield]: https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-2.svg [buymeacoffee]: https://www.buymeacoffee.com/timmo [CODE_OF_CONDUCT]: https://github.com/timmo001/ovoenergy/blob/master/.github/CODE_OF_CONDUCT.md [CONTRIBUTING]: https://github.com/timmo001/ovoenergy/blob/master/.github/CONTRIBUTING.md [license-shield]: https://img.shields.io/github/license/timmo001/ovoenergy.svg [project-stage-shield]: https://img.shields.io/badge/project%20stage-beta-blue.svg [pulls-shield]: https://img.shields.io/docker/pulls/timmo001/ovoenergy.svg [releases-shield]: https://img.shields.io/github/release/timmo001/ovoenergy.svg [releases]: https://github.com/timmo001/ovoenergy/releases timmo001-ovoenergy-38e0018/mlc_config.json000066400000000000000000000004131420076414200203360ustar00rootroot00000000000000{ "ignorePatterns": [ { "pattern": "^http://localhost" }, { "pattern": "^contact@timmo" }, { "pattern": "^https://www.buymeacoffee.com" }, { "pattern": "^https://microbadger.com" } ], "retryOn429": true } timmo001-ovoenergy-38e0018/ovoenergy/000077500000000000000000000000001420076414200173625ustar00rootroot00000000000000timmo001-ovoenergy-38e0018/ovoenergy/__init__.py000066400000000000000000000035641420076414200215030ustar00rootroot00000000000000"""Initialize the package.""" from datetime import datetime from typing import List class OVOInterval: def __init__(self, start: datetime, end: datetime): self.start = start self.end = end class OVOMeterReadings: def __init__(self, start: float, end: float): self.start = start self.end = end class OVOCost: def __init__(self, amount: float, currency_unit: str): self.amount = amount self.currency_unit = currency_unit class OVODailyElectricity: def __init__( self, consumption: float, interval: OVOInterval, meter_readings: OVOMeterReadings, has_half_hour_data: bool, cost: OVOCost, ): self.consumption = consumption self.interval = interval self.meter_readings = meter_readings self.has_half_hour_data = has_half_hour_data self.cost = cost class OVODailyGas: def __init__( self, consumption: float, volume: float, interval: OVOInterval, meter_readings: OVOMeterReadings, has_half_hour_data: bool, cost: OVOCost, ): self.consumption = consumption self.volume = volume self.interval = interval self.meter_readings = meter_readings self.has_half_hour_data = has_half_hour_data self.cost = cost class OVOHalfHour: def __init__(self, consumption: float, interval: OVOInterval, unit: str): self.consumption = consumption self.interval = interval self.unit = unit class OVODailyUsage: def __init__(self, electricity: List[OVODailyElectricity], gas: List[OVODailyGas]): self.electricity = electricity self.gas = gas class OVOHalfHourUsage: def __init__(self, electricity: List[OVOHalfHour], gas: List[OVOHalfHour]): self.electricity = electricity self.gas = gas timmo001-ovoenergy-38e0018/ovoenergy/cli.py000066400000000000000000000075701420076414200205140ustar00rootroot00000000000000"""Enable CLI.""" import asyncio import click from ovoenergy.ovoenergy import OVOEnergy @click.command() @click.option("--username", "-u", help="Username") @click.option("--password", "-p", help="Password") @click.option("--account", "-a", help="Account Number") @click.option("--date", "-D", help="Date") @click.option("--daily", "-d", is_flag=True, help="Daily usage") @click.option("--halfhour", "-h", is_flag=True, help="Half hourly usage") def cli(username, password, account, date, daily=True, halfhour=False): """CLI for this package.""" asyncio.run(handle(username, password, account, date, daily, halfhour)) async def handle(username, password, account, date, daily=True, halfhour=False) -> None: client = OVOEnergy() authenticated = await client.authenticate(username, password, account) print(f"Authenticated: {authenticated}") if authenticated: print("Authenticated.") if daily is True: usage = await client.get_daily_usage(date) if usage is not None: print("Usage:") print(usage) if usage.electricity is not None: print("Electricity:") count = 0 for x in usage.electricity: count += 1 print(f"{count}.consumption: {x.consumption}") print(f"{count}.interval.start: {x.interval.start}") print(f"{count}.interval.end: {x.interval.end}") # print(f"{count}.meter_readings.start: {x.meter_readings.start}") # print(f"{count}.meter_readings.end: {x.meter_readings.end}") print(f"{count}.has_hh_data: {x.has_half_hour_data}") print(f"{count}.cost.amount: {x.cost.amount}") print(f"{count}.cost.currency_unit: {x.cost.currency_unit}") if usage.gas is not None: print("Gas:") count = 0 for x in usage.gas: count += 1 print(f"{count}.consumption: {x.consumption}") print(f"{count}.volume: {x.volume}") print(f"{count}.interval.start: {x.interval.start}") print(f"{count}.interval.end: {x.interval.end}") # print(f"{count}.meter_readings.start: {x.meter_readings.start}") # print(f"{count}.meter_readings.end: {x.meter_readings.end}") print(f"{count}.has_hh_data: {x.has_half_hour_data}") print(f"{count}.cost.amount: {x.cost.amount}") print(f"{count}.cost.currency_unit: {x.cost.currency_unit}") if halfhour is True: usage = await client.get_half_hourly_usage(date) if usage is not None: print("Usage:") print(usage) if usage.electricity is not None: print("Electricity:") count = 0 for x in usage.electricity: count += 1 print(f"consumption: {x.consumption}") print(f"{count}.interval.start: {x.interval.start}") print(f"{count}.interval.end: {x.interval.end}") print(f"{count}.unit: {x.unit}") if usage.gas is not None: print("Gas:") count = 0 for x in usage.gas: count += 1 print(f"{count}.consumption: {x.consumption}") print(f"{count}.interval.start: {x.interval.start}") print(f"{count}.interval.end: {x.interval.end}") print(f"{count}.unit: {x.unit}") cli() # pylint: disable=E1120 timmo001-ovoenergy-38e0018/ovoenergy/ovoenergy.py000066400000000000000000000257231420076414200217620ustar00rootroot00000000000000"""Get energy data from OVO's API.""" from datetime import datetime, timedelta import aiohttp from ovoenergy import ( OVOCost, OVODailyElectricity, OVODailyGas, OVODailyUsage, OVOHalfHour, OVOHalfHourUsage, OVOInterval, OVOMeterReadings, ) class OVOEnergy: """Class for OVOEnergy.""" def __init__(self) -> None: """Initilalize.""" self._account_id = None self._account_ids = None self._cookies = None self._customer_id = None self._username = None async def authenticate(self, username, password, account=None) -> bool: """Authenticate.""" async with aiohttp.ClientSession() as session: response = await session.post( "https://my.ovoenergy.com/api/v2/auth/login", json={ "username": username, "password": password, "rememberMe": True, }, ) try: response.raise_for_status() except: pass if response.status is not 200: return False json_response = await response.json() if "code" in json_response and json_response["code"] == "Unknown": return False else: self._cookies = response.cookies response = await session.get( "https://smartpaym.ovoenergy.com/api/customer-and-account-ids", cookies=self._cookies, ) json_response = await response.json() if "accountIds" in json_response: self._account_ids = json_response["accountIds"] if account: if account in self._account_ids: self._account_id = account else: return False else: self._account_id = self._account_ids[0] if "customerId" in json_response: self._customer_id = json_response["customerId"] self._username = username return True async def get_daily_usage(self, month: str) -> OVODailyUsage: """Get daily usage data.""" if month is None: return None electricity_usage = None gas_usage = None async with aiohttp.ClientSession() as session: response = await session.get( f"https://smartpaym.ovoenergy.com/api/energy-usage/daily/{self._account_id}?date={month}", cookies=self._cookies, ) json_response = await response.json() if "electricity" in json_response: electricity = json_response["electricity"] if electricity and "data" in electricity: electricity_usage = [] for usage in electricity["data"]: if usage is not None: electricity_usage.append( OVODailyElectricity( usage["consumption"] if "consumption" in usage else None, OVOInterval( datetime.strptime( usage["interval"]["start"], "%Y-%m-%dT%H:%M:%S.%f", ), datetime.strptime( usage["interval"]["end"], "%Y-%m-%dT%H:%M:%S.%f" ), ) if "interval" in usage and "start" in usage["interval"] and "end" in usage["interval"] else None, OVOMeterReadings( usage["meterReadings"]["start"], usage["meterReadings"]["end"], ) if "meterReadings" in usage else None, usage["hasHhData"] if "hasHhData" in usage else None, OVOCost( usage["cost"]["amount"], usage["cost"]["currencyUnit"], ) if "cost" in usage else None, ) ) if "gas" in json_response: gas = json_response["gas"] if gas and "data" in gas: gas_usage = [] for usage in gas["data"]: if usage is not None: gas_usage.append( OVODailyGas( usage["consumption"] if "consumption" in usage else None, usage["volume"] if "volume" in usage else None, OVOInterval( datetime.strptime( usage["interval"]["start"], "%Y-%m-%dT%H:%M:%S.%f", ), datetime.strptime( usage["interval"]["end"], "%Y-%m-%dT%H:%M:%S.%f" ), ) if "interval" in usage and "start" in usage["interval"] and "end" in usage["interval"] else None, OVOMeterReadings( usage["meterReadings"]["start"], usage["meterReadings"]["end"], ) if "meterReadings" in usage else None, usage["hasHhData"] if "hasHhData" in usage else None, OVOCost( usage["cost"]["amount"], usage["cost"]["currencyUnit"], ) if "cost" in usage else None, ) ) return OVODailyUsage(electricity_usage, gas_usage) async def get_half_hourly_usage(self, date: str) -> OVOHalfHourUsage: """Get half hourly usage data.""" if date is None: return None electricity_usage = None gas_usage = None async with aiohttp.ClientSession() as session: response = await session.get( f"https://smartpaym.ovoenergy.com/api/energy-usage/half-hourly/{self._account_id}?date={date}", cookies=self._cookies, ) json_response = await response.json() if "electricity" in json_response: electricity = json_response["electricity"] if electricity and "data" in electricity: electricity_usage = [] for usage in electricity["data"]: if usage is not None: electricity_usage.append( OVOHalfHour( usage["consumption"] if "consumption" in usage else None, OVOInterval( datetime.strptime( usage["interval"]["start"], "%Y-%m-%dT%H:%M:%S.%f", ), datetime.strptime( usage["interval"]["end"], "%Y-%m-%dT%H:%M:%S.%f" ), ) if "interval" in usage and "start" in usage["interval"] and "end" in usage["interval"] else None, usage["unit"] if "unit" in usage else None, ) ) if "gas" in json_response: gas = json_response["gas"] if gas and "data" in gas: gas_usage = [] for usage in gas["data"]: if usage is not None: gas_usage.append( OVOHalfHour( usage["consumption"] if "consumption" in usage else None, OVOInterval( datetime.strptime( usage["interval"]["start"], "%Y-%m-%dT%H:%M:%S.%f", ), datetime.strptime( usage["interval"]["end"], "%Y-%m-%dT%H:%M:%S.%f" ), ) if "interval" in usage and "start" in usage["interval"] and "end" in usage["interval"] else None, usage["unit"] if "unit" in usage else None, ) ) return OVOHalfHourUsage(electricity_usage, gas_usage) async def get_last_reading(self, date: datetime = None) -> OVOHalfHourUsage: date = date if date is not None else datetime.utcnow() print(f"DATE: {date}") usage: OVOHalfHourUsage = await self.get_half_hourly_usage( date.strftime("%Y-%m-%d") ) if usage is None or usage.electricity is None or len(usage.electricity) == 0: date = date - timedelta(days=1) if date is datetime.utcnow() - timedelta(days=7): return None return await self.get_last_reading(date) return usage @property def account_id(self): return self._account_id @account_id.setter def account_id(self, new_account): if new_account in self._account_ids: self._account_id = new_account else: print("Invalid account ID") @property def account_ids(self): return self._account_ids @property def customer_id(self): return self._customer_id @property def username(self): return self._username timmo001-ovoenergy-38e0018/pyvenv.cfg000066400000000000000000000001051420076414200173510ustar00rootroot00000000000000home = /usr/bin include-system-site-packages = false version = 3.7.5 timmo001-ovoenergy-38e0018/renovate.json000066400000000000000000000003561420076414200200670ustar00rootroot00000000000000{ "extends": ["config:base"], "automerge": true, "major": { "automerge": false }, "commitMessagePrefix": "⬆", "pin": { "commitMessagePrefix": "πŸ“Œ" }, "rangeStrategy": "pin", "labels": ["Type: Maintenance"] } timmo001-ovoenergy-38e0018/requirements.txt000066400000000000000000000000341420076414200206260ustar00rootroot00000000000000aiohttp>=3.7.3 click>=7.1.2 timmo001-ovoenergy-38e0018/setup.cfg000066400000000000000000000000471420076414200171670ustar00rootroot00000000000000[metadata] description-file = README.mdtimmo001-ovoenergy-38e0018/setup.py000066400000000000000000000013671420076414200170660ustar00rootroot00000000000000"""Setup configuration.""" import setuptools with open("README.md", "r") as fh: LONG = fh.read() setuptools.setup( name="ovoenergy", version="1.2.0", author="Timmo", author_email="contact@timmo.xyz", description="Get energy data from OVO's API", license="MIT", long_description=LONG, long_description_content_type="text/markdown", install_requires=["aiohttp>=3.7.3", "click>=7.1.2"], entry_points={"console_scripts": ["ovoenergy = ovoenergy.cli:cli"]}, url="https://github.com/timmo001/ovoenergy", packages=setuptools.find_packages(), classifiers=( "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ), )