commit d5a422ca10ef1366ba574496df22305fa51eea00 Author: Scott Wallace Date: Tue Aug 3 15:56:28 2021 +0100 Initial code commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad3dffd --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode/ +__pycache__/ +.pyenv/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a8692bc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.8-slim-buster + +# Keeps Python from generating .pyc files in the container +ENV PYTHONDONTWRITEBYTECODE 1 + +# Turns off buffering for easier container logging +ENV PYTHONUNBUFFERED 1 + +ADD requirements.txt . +RUN python -m pip install -r requirements.txt + +WORKDIR /app +COPY main.py /app + +RUN useradd appuser && chown -R appuser /app +USER appuser + +EXPOSE 5000 + +ENTRYPOINT ["python", "main.py"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..95d1f04 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Carbon Exporter +Pulls carbon intensity data from [carbonintensity.org.uk](https://carbonintensity.org.uk) and provides it in Prometheus-style exporter format over HTTP. + +Used to monitor a UK region's carbon intensity over time. diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..5551557 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,9 @@ +--- +version: '3.7' +services: + carbon-exporter: + image: carbon-exporter:latest + container_name: carbon-exporter + environment: + - CARBON_POSTCODE= + restart: unless-stopped diff --git a/main.py b/main.py new file mode 100644 index 0000000..3f3a441 --- /dev/null +++ b/main.py @@ -0,0 +1,79 @@ +""" +Present observational weather data to Prometheus +""" + +import json +import os +from typing import Dict, List + +import requests # type: ignore +from flask import Flask, Response # type: ignore + +app = Flask(__name__) + + +def fetch_carbon_data(postcode: str) -> Dict: + """ + Fetch current data for the carbon intensity of the provided region + """ + + obs_data = requests.get( + f'https://api.carbonintensity.org.uk/regional/postcode/{postcode}' + ) + + return json.loads(obs_data.content) + + +@app.route('/metrics') +def metrics(): + """ + Output Prometheus-style metrics + """ + postcode = os.environ.get('CARBON_POSTCODE') + + latest_data = fetch_carbon_data(postcode)['data'][0] + + ret_data: List = list() + for generation in latest_data['data'][0]['generationmix']: + ret_data.append( + { + 'key': f'sensor_carbon_generation_{generation["fuel"]}_perc', + 'labels': { + 'region': latest_data['dnoregion'], + }, + 'type': 'gauge', + 'value': float(generation['perc']) + } + ) + + ret_data.append( + { + 'key': 'sensor_carbon_intensity_forecast', + 'labels': { + 'region': latest_data['dnoregion'], + }, + 'type': 'gauge', + 'value': float(latest_data['data'][0]['intensity']['forecast']), + } + ) + + ret_strs = list() + for item in ret_data: + ret_strs.append(f'# HELP {item["key"]} Carbon metric') + ret_strs.append(f'# TYPE {item["key"]} {item["type"]}') + ret_strs.append( + item["key"] + + '{' + + " ".join([f'{key}="{val}"' for key, val in item["labels"].items()]) + + '} ' + + str(item["value"]) + ) + + resp = Response('\n'.join(ret_strs)) + resp.headers['Content-type'] = 'text/plain' + + return resp + + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..30692b7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +flask +requests