Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Publish to PyPI

on:
push:
tags:
- "v*"

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed for setuptools_scm to derive version from tags

- uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install build dependencies
run: pip install build

- name: Build distributions
run: python -m build

- name: Upload distributions as artifact
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/carconnectivity-plugin-campermode
permissions:
id-token: write # required for OIDC trusted publisher

steps:
- name: Download distributions
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ description = "CarConnectivity plugin for overnight climatisation cycling (campe
dynamic = ["version"]
requires-python = ">=3.10"
authors = [{ name = "Andreas Schneider" }]
keywords = ["carconnectivity", "volkswagen", "vw", "climatisation", "camper"]
dependencies = [
"carconnectivity>=0.11.5",
"Werkzeug~=3.1.3",
Expand All @@ -22,8 +23,22 @@ license-files = ["LICENSE"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Topic :: Home Automation",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]

[project.urls]
Homepage = "https://github.com/cryptomilk/CarConnectivity-plugin-campermode"
Repository = "https://github.com/cryptomilk/CarConnectivity-plugin-campermode"
Issues = "https://github.com/cryptomilk/CarConnectivity-plugin-campermode/issues"

[project.entry-points."carconnectivity.plugins"]
campermode = "carconnectivity_plugins.campermode.plugin:Plugin"

[tool.setuptools.package-data]
"carconnectivity_plugins.campermode" = [
"ui/templates/campermode/*.html",
Expand Down
22 changes: 7 additions & 15 deletions src/carconnectivity_plugins/campermode/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from dataclasses import dataclass, field, replace
from datetime import datetime, time
from enum import Enum
from typing import cast
from typing import Any, cast

LOG: logging.Logger = logging.getLogger("carconnectivity.plugins.campermode")

Expand Down Expand Up @@ -133,18 +133,12 @@ def to_dict(self) -> dict[str, object]:
}

@classmethod
def from_dict(cls, data: dict[str, object]) -> CamperSettings:
def from_dict(cls, data: dict[str, Any]) -> CamperSettings:
return cls(
min_battery_level=int(data.get("min_battery_level", 20)), # type: ignore[arg-type]
minutes_between_cycles=int(
data.get("minutes_between_cycles", 0) # type: ignore[arg-type]
),
cycle_duration_minutes=int(
data.get("cycle_duration_minutes", 30) # type: ignore[arg-type]
),
total_duration_minutes=int(
data.get("total_duration_minutes", 60) # type: ignore[arg-type]
),
min_battery_level=int(data.get("min_battery_level", 20)),
minutes_between_cycles=int(data.get("minutes_between_cycles", 0)),
cycle_duration_minutes=int(data.get("cycle_duration_minutes", 30)),
total_duration_minutes=int(data.get("total_duration_minutes", 60)),
endless=bool(data.get("endless", False)),
extra_heating_enabled=bool(
data.get("extra_heating_enabled", False)
Expand All @@ -155,9 +149,7 @@ def from_dict(cls, data: dict[str, object]) -> CamperSettings:
front_zone_right=bool(data.get("front_zone_right", False)),
rear_zone_left=bool(data.get("rear_zone_left", False)),
rear_zone_right=bool(data.get("rear_zone_right", False)),
target_temperature=float(
data.get("target_temperature", 22.0) # type: ignore[arg-type]
),
target_temperature=float(data.get("target_temperature", 22.0)),
)


Expand Down
2 changes: 1 addition & 1 deletion src/carconnectivity_plugins/campermode/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def _apply_climate_settings(self) -> None:

# VW-specific per-zone seat/window heating
try:
from carconnectivity_connectors.volkswagen.climatization import ( # type: ignore[import-not-found]
from carconnectivity_connectors.volkswagen.climatization import ( # ty: ignore[unresolved-import]
VolkswagenClimatization,
)

Expand Down
14 changes: 8 additions & 6 deletions src/carconnectivity_plugins/campermode/ui/plugin_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class LoginForm(FlaskForm):
submit = SubmitField("Login")


class _User(flask_login.UserMixin):
def __init__(self, username: str) -> None:
self.id: str = username


class CamperUI:
"""Standalone Flask web server for the CamperMode plugin."""

Expand Down Expand Up @@ -94,8 +99,7 @@ def _user_loader(
) -> flask_login.UserMixin | None:
if username not in self.users:
return None
user = flask_login.UserMixin()
user.id = username # type: ignore[assignment]
user = _User(username)
return user

@login_manager.request_loader
Expand All @@ -114,8 +118,7 @@ def _load_from_request(
username, password = decoded.split(":", 1)
stored = self.users.get(username, {}).get("password", "")
if hmac.compare_digest(stored, password):
user = flask_login.UserMixin()
user.id = username # type: ignore[assignment]
user = _User(username)
return user
return None

Expand Down Expand Up @@ -165,8 +168,7 @@ def login() -> WerkzeugResponse | str:
if stored_pwd and hmac.compare_digest(
stored_pwd, form.password.data or ""
):
user = flask_login.UserMixin()
user.id = username # type: ignore[assignment]
user = _User(username)
flask_login.login_user(
user, remember=bool(form.remember_me.data)
)
Expand Down
Loading