Metadata-Version: 2.5
Name: window-time
Version: 0.2.0
Summary: A daemon that monitors indoor/outdoor temperatures and notifies when to open or close windows
Project-URL: Homepage, https://github.com/gene1wood/window-time
Project-URL: Repository, https://github.com/gene1wood/window-time
Project-URL: Issues, https://github.com/gene1wood/window-time/issues
Author: Gene Wood
License-Expression: MIT
Keywords: home-automation,influxdb,notifications,temperature
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Home Automation
Requires-Python: >=3.10
Requires-Dist: influxdb3-python>=0.21.0
Requires-Dist: numpy<=2.3.5,>=1.24.0
Requires-Dist: platformdirs>=4.0.0
Requires-Dist: pyarrow>=14.0.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# window-time

A daemon that monitors indoor/outdoor temperatures from InfluxDB and sends notifications when it's time to open or close windows based on the day's forecast.

## Installation

```bash
pip install window-time
```

## Quick Start

```bash
# Initialize configuration file
window-time init

# Edit ~/.config/window-time/config.yaml with your settings
# Then run the daemon
window-time run
```

## Usage

```bash
# Run the daemon (checks every 5 minutes)
window-time run

# Run a single check
window-time once

# Show current temperatures and status
window-time status

# Show configuration paths
window-time config

# Show configuration values
window-time config -s

# Enable verbose console logging
window-time -v run

# Log decisions to a file
window-time --log-file /var/log/window-time.log run

# Log decisions with verbose underlying data
window-time --log-file /var/log/window-time.log --log-verbose run

# Use a custom config file
window-time -c /path/to/config.yaml run
```

## Configuration

Configuration is stored in `~/.config/window-time/config.yaml` (or equivalent XDG path on your OS).

```yaml
influxdb:
  url: http://localhost:8181
  token: your-influxdb-token-here
  database: home
  measurement: environment
  temperature_field: temperature
  domain_tag: domain
  location_tag: location
  mac_tag: mac
  indoor_domain: indoors
  outdoor_domain: outdoors

notification:
  ntfy_topic: your-ntfy-topic-here
  ntfy_server: https://ntfy.sh

thresholds:
  hot_day_high_temp: 75.0   # Forecast high >= this → hot day (retain cool air)
  cold_day_high_temp: 60.0  # Forecast high <= this → cold day (retain warm air)
                            # Between the two → moderate day (no alerts)

daemon:
  interval_minutes: 5
  lookback_hours: 1

logging:
  log_file: ""           # Path to log file (empty = no file logging)
  log_verbose: false     # Include underlying temperature data in logs

location:
  latitude: null         # Set to override auto-detection from IP
  longitude: null
```

The InfluxDB token can also be set via the `INFLUXDB_TOKEN` environment variable.

### Logging

The logging section enables decision logging to a file. This is useful for reviewing the daemon's decisions over time.

- **log_file**: Path to a log file. When set, decisions are logged to this file. Leave empty to disable file logging.
- **log_verbose**: When `true`, logs include detailed underlying data (temperatures, trends, state transitions) in addition to the decision and rationale.

Command-line arguments `--log-file` and `--log-verbose` override config file settings.

Example log output (standard):
```
2024-07-15 14:30:00 - INFO - DECISION: CLOSE | Day mode: hot | Reason: Hot day: outdoor temperature rose above indoor. Close windows to retain cool air inside.
```

Example log output (verbose):
```
2024-07-15 14:30:00 - INFO - DECISION: CLOSE | Day mode: hot | Reason: Hot day: outdoor temperature rose above indoor. Close windows to retain cool air inside.
2024-07-15 14:30:00 - DEBUG -   Temperature data: indoor=68.5° outdoor=70.2° outdoor_warmer=True
2024-07-15 14:30:00 - DEBUG -   State transition: last_outdoor_warmer: False -> True | open_alert_sent: False -> False | close_alert_sent: False -> True | last_known_state: unknown -> closed
2024-07-15 14:30:00 - DEBUG -   Indoor trend: rising (+0.25°/hr, confidence=0.89)
2024-07-15 14:30:00 - DEBUG -   Outdoor trend: rising (+1.50°/hr, confidence=0.95)
```

## Setting up InfluxDB 3 Core

InfluxDB 3 Core has no concept of organizations, buckets, or per-user accounts — it uses
databases and bearer tokens managed with the `influxdb3` CLI.

* Create the database `influxdb3 create database [my-database-name]`
* Create a token `influxdb3 create token --admin`

  Core does not support scoped/read-only tokens (that's an Enterprise/Cloud feature), so the
  admin token returned here is what you put in `influxdb.token`.

## Running as a systemd Service

To run window-time as a user service that starts automatically:

1. **Copy the service file:**

   ```bash
   mkdir -p ~/.config/systemd/user
   cp window-time.service ~/.config/systemd/user/
   ```

   Or if installed via pip, create the file manually:

   ```bash
   cat > ~/.config/systemd/user/window-time.service << 'EOF'
   [Unit]
   Description=Window Time - Temperature-based window notification daemon
   After=network-online.target
   Wants=network-online.target

   [Service]
   Type=simple
   ExecStart=%h/.local/bin/window-time run
   Restart=on-failure
   RestartSec=30

   [Install]
   WantedBy=default.target
   EOF
   ```

   Note: Adjust `ExecStart` path to match your installation (e.g., `/usr/local/bin/window-time` or `%h/.local/bin/window-time`).

2. **Optionally create an environment file for secrets:**

   ```bash
   cat > ~/.config/window-time/env << 'EOF'
   INFLUXDB_TOKEN=your-token-here
   EOF
   chmod 600 ~/.config/window-time/env
   ```

   Then add to the `[Service]` section:
   ```
   EnvironmentFile=%h/.config/window-time/env
   ```

3. **Enable and start the service:**

   ```bash
   systemctl --user daemon-reload
   systemctl --user enable window-time
   systemctl --user start window-time
   ```

4. **Check status:**

   ```bash
   systemctl --user status window-time
   journalctl --user -u window-time -f
   ```

5. **Enable lingering (to run service without being logged in):**

   ```bash
   loginctl enable-linger $USER
   ```

## How It Works

### Day Classification

Each morning the daemon fetches the day's forecast high temperature and classifies the day into one of three modes:

- **Hot day** (forecast high ≥ `hot_day_high_temp`, default 75°F): The goal is to retain cool air inside. Windows are closed when outdoor rises above indoor, and opened when outdoor falls below indoor in the evening.
- **Cold day** (forecast high ≤ `cold_day_high_temp`, default 60°F): The goal is to retain warm air inside. Windows are opened when outdoor rises above indoor (e.g., a sunny afternoon), and closed again when outdoor falls back below indoor.
- **Moderate day** (forecast high between the two thresholds): No window management is needed and no alerts are sent.

### Alert Deduplication

At most one open alert and one close alert are sent per day. If temperatures oscillate and cross multiple times in a day, only the first qualifying crossing triggers an alert.

### Forecast

The daily forecast is fetched once from [Open-Meteo](https://open-meteo.com/) at the start of each day. Location is auto-detected from the server's public IP address via [ip-api.com](http://ip-api.com/), or can be configured manually with `latitude` and `longitude` in the config file.

## InfluxDB Data Format

The tool expects temperature data with the following structure:

- **Measurement**: `environment` (configurable)
- **Field**: `temperature` (configurable)
- **Tags**:
  - `domain`: Either `indoors` or `outdoors`
  - `location`: Description of sensor location
  - `mac`: Bluetooth MAC address of the sensor

## License

MIT
