juham-visualcrossing 0.0.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ """
2
+ Description
3
+ ===========
4
+
5
+ VisualCrossing weather forecast.
6
+
7
+ """
8
+
9
+ from .visualcrossing import VisualCrossing
10
+
11
+ __all__ = [
12
+ "VisualCrossing",
13
+ ]
@@ -0,0 +1,180 @@
1
+ from datetime import datetime, timedelta, timezone
2
+ import json
3
+
4
+ from typing import Any
5
+ from juham.base import Base
6
+ from juham.web import RCloud, RCloudThread
7
+
8
+
9
+ class VisualCrossingThread(RCloudThread):
10
+ """Asynchronous thread for acquiring forecast from the VisualCrossing
11
+ site."""
12
+
13
+ # class attributes
14
+ _forecast_topic = ""
15
+ _base_url = ""
16
+ _api_key = ""
17
+ _location = ""
18
+ _interval: float = 12 * 3600
19
+
20
+ def __init__(self, client=None):
21
+ """Construct with the given mqtt client. Acquires data from the visual
22
+ crossing web service and publishes the forecast data to
23
+ forecast_topic.
24
+
25
+ Args:
26
+ client (object, optional): MQTT client. Defaults to None.
27
+ """
28
+ super().__init__(client)
29
+ self.mqtt_client = client
30
+
31
+ def update_interval(self) -> float:
32
+ return self._interval
33
+
34
+ # @override
35
+ def make_url(self) -> str:
36
+ if not self._api_key:
37
+ self.error("Uninitialized api_key {self.get_class_id()}: {self._api_key}")
38
+ return ""
39
+ else:
40
+ now = datetime.now()
41
+ end = now + timedelta(days=1)
42
+ start = now.strftime("%Y-%m-%d")
43
+ stop = end.strftime("%Y-%m-%d")
44
+ url = f"{self._base_url}{self._location}/{start}/{stop}?unitGroup=metric&contentType=json&include=hours&key={self._api_key}"
45
+ self.debug(url)
46
+ return url
47
+
48
+ def init(
49
+ self, topic: str, base_url: str, interval: float, api_key: str, location: str
50
+ ) -> None:
51
+ """Initialize the data acquisition thread
52
+
53
+ Args:
54
+ topic (str): mqtt topic to publish the acquired data
55
+ base_url (str): url of the web service
56
+ interval (float): update interval in seconds
57
+ api_key (str): api_key, as required by the web service
58
+ location (str): geographic location
59
+ """
60
+ self._forecast_topic = topic
61
+ self._base_url = base_url
62
+ self._interval = interval
63
+ self._api_key = api_key
64
+ self._location = location
65
+
66
+ # @override
67
+ def process_data(self, data: Any):
68
+ self.info("VisualCrossing process_data()")
69
+ data = data.json()
70
+ forecast = []
71
+ self.info(f"VisualCrossing {data}")
72
+ for day in data["days"]:
73
+ for hour in day["hours"]:
74
+ ts = int(hour["datetimeEpoch"])
75
+ forecast.append(
76
+ {
77
+ "id": "visualcrossing",
78
+ "ts": ts,
79
+ "hour": datetime.fromtimestamp(ts, tz=timezone.utc).strftime(
80
+ "%H"
81
+ ),
82
+ "day": datetime.fromtimestamp(ts, tz=timezone.utc).strftime(
83
+ "%Y%m%d%H"
84
+ ),
85
+ "uvindex": hour["uvindex"],
86
+ "solarradiation": hour["solarradiation"],
87
+ "solarenergy": hour["solarenergy"],
88
+ "cloudcover": hour["cloudcover"],
89
+ "snow": hour["snow"],
90
+ "snowdepth": hour["snowdepth"],
91
+ "pressure": hour["pressure"],
92
+ "temp": hour["temp"],
93
+ "humidity": hour["humidity"],
94
+ "windspeed": hour["windspeed"],
95
+ "winddir": hour["winddir"],
96
+ "dew": hour["dew"],
97
+ }
98
+ )
99
+ msg = json.dumps(forecast)
100
+ self.publish(self._forecast_topic, msg, qos=1, retain=False)
101
+ self.info(f"VisualCrossing forecast published to {self._forecast_topic}")
102
+
103
+
104
+ class VisualCrossing(RCloud):
105
+ """Constructs a data acquisition object for reading weather
106
+ forecasts from the VisualCrossing web service. Subscribes to the
107
+ forecast topic and writes hourly data such as solar energy, temperature,
108
+ and other attributes relevant to home automation into a time series
109
+ database.
110
+
111
+ Spawns an asynchronous thread to run queries at the specified
112
+ update_interval.
113
+ """
114
+
115
+ workerThreadId = VisualCrossingThread.get_class_id()
116
+ forecast_topic = Base.mqtt_root_topic + "/forecast"
117
+ base_url = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/"
118
+ update_interval = 12 * 3600
119
+ api_key = "SE9W7EHP775N7NDNW8ANM2MZN"
120
+ location = "lahti,finland"
121
+
122
+ def __init__(self, name="visualcrossing"):
123
+ super().__init__(name)
124
+ self.debug(f"VisualCrossing created")
125
+
126
+ def on_connect(self, client, userdata, flags, rc):
127
+ super().on_connect(client, userdata, flags, rc)
128
+ if rc == 0:
129
+ self.subscribe(self.forecast_topic)
130
+ self.debug(f"VisualCrossing subscribed to topic {self.forecast_topic}")
131
+
132
+ def on_message(self, client, userdata, msg):
133
+ if msg.topic == self.forecast_topic:
134
+ em = json.loads(msg.payload.decode())
135
+ self.on_forecast(em)
136
+ else:
137
+ super().on_message(client, userdata, msg)
138
+
139
+ def on_forecast(self, em: dict) -> None:
140
+ """Handle weather forecast data.
141
+
142
+ Args:
143
+ em (dict): forecast
144
+ """
145
+ self.debug(f"VisualCrossing: got mqtt message {em}")
146
+
147
+ # @override
148
+ def run(self):
149
+ # create, initialize and start the asynchronous thread for acquiring forecast
150
+ self.worker = Base.instantiate(VisualCrossing.workerThreadId)
151
+ self.worker.init(
152
+ self.forecast_topic,
153
+ self.base_url,
154
+ self.update_interval,
155
+ self.api_key,
156
+ self.location,
157
+ )
158
+ self.debug(f"VisualCrossing.run(): base_url is {self.base_url}")
159
+ self.debug(f"VisualCrossing.run(): interval is {self.update_interval}")
160
+ self.debug(f"VisualCrossing.run(): api_key is {self.api_key}")
161
+ self.debug(f"VisualCrossing.run(): location is {self.location}")
162
+ super().run()
163
+
164
+ # @override
165
+ def to_dict(self):
166
+ data = super().to_dict()
167
+ data["_visualcrossing"] = {
168
+ "topic": self.forecast_topic,
169
+ "url": self.base_url,
170
+ "api_key": self.api_key,
171
+ "interval": self.update_interval,
172
+ }
173
+ return data
174
+
175
+ # @override
176
+ def from_dict(self, data):
177
+ super().from_dict(data)
178
+ if "_visualcrossing" in data:
179
+ for key, value in data["_visualcrossing"].items():
180
+ setattr(self, key, value)
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.1
2
+ Name: juham_visualcrossing
3
+ Version: 0.0.1
4
+ Summary: A Weather forecast plugin extending `juham` applications
5
+ Author-email: J Meskanen <juham.api@gmail.com>
6
+ Maintainer-email: "J. Meskanen" <juham.api@gmail.com>
7
+ License: MIT License
8
+ ===========
9
+
10
+ Copyright (c) 2024, Juha Meskanen
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+
29
+
30
+ Project-URL: Homepage, https://meskanen.com
31
+ Project-URL: Bug Reports, https://meskanen.com
32
+ Project-URL: Funding, https://meskanen.com
33
+ Project-URL: Say Thanks!, http://meskanen.com
34
+ Project-URL: Source, https://meskanen.com
35
+ Keywords: object-oriented,plugin,framework
36
+ Classifier: Development Status :: 1 - Planning
37
+ Classifier: Intended Audience :: Developers
38
+ Classifier: Topic :: Software Development
39
+ Classifier: License :: OSI Approved :: MIT License
40
+ Classifier: Programming Language :: Python :: 3.8
41
+ Requires-Python: >=3.8
42
+ Description-Content-Type: text/markdown
43
+ Requires-Dist: juham >=0.0.7
44
+ Requires-Dist: pytz >=2024.1
45
+ Requires-Dist: importlib-metadata
46
+ Provides-Extra: dev
47
+ Requires-Dist: check-manifest ; extra == 'dev'
48
+
49
+ juham_visualcrossing
50
+ ====================
51
+
52
+ `juham_visualcrossing` plugs VisualCrossing weather forecast service to Juham - Juha's Ultimate
53
+ Home Automation Masterpiece application.
54
+
55
+
56
+
57
+ Features
58
+ --------
59
+
60
+ The main purpose is to acquire solar energy forecast, for optimizing energy management in
61
+ homes with solar panels.
62
+
63
+
64
+ Installation
65
+ ------------
66
+
67
+ The installation is two stage process
68
+
69
+ 1. To install:
70
+
71
+ .. code-block:: python
72
+
73
+ pip install juham_visualcrossing
74
+
75
+
76
+ 2. Configure
77
+
78
+ VisualCrossing requires you to register and obtain web key, through which you can
79
+ access their forecast services. So sign in to visualcrossing.com to obtain the key,
80
+ and then add the web key to the '~/.[app]/config/VisualCrossing.json' configuration
81
+ file.
82
+
83
+
84
+
85
+ License
86
+ -------
87
+
88
+ This project is licensed under the MIT License - see the `LICENSE` file for details.
@@ -0,0 +1,7 @@
1
+ juham_visualcrossing/__init__.py,sha256=qsVRSTeJW8jgBtLviUjq5JiJ8Z6eYkBQn6zMWgS0okY,161
2
+ juham_visualcrossing/visualcrossing.py,sha256=W-FPKSJJ0IvxhKZhYOzmswbpu3Uuy6EZqE1r_Xjev14,6678
3
+ juham_visualcrossing-0.0.1.dist-info/METADATA,sha256=865kbtRSCr1FqX-Tveq4kDyfZJchYhARu6bAyL6GFaM,3154
4
+ juham_visualcrossing-0.0.1.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
5
+ juham_visualcrossing-0.0.1.dist-info/entry_points.txt,sha256=A-NSi2RN5KSKwpbKG592v5qnWnGHmIjPktubd5PQ80M,76
6
+ juham_visualcrossing-0.0.1.dist-info/top_level.txt,sha256=7yWaUHZo-Ng96VJBzUFhaFCBamL71PUjIxZISxMBjSM,21
7
+ juham_visualcrossing-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.2.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [juham.plugins]
2
+ visualcrossing_plugin = juham_visualcrossing:VisualCrossing
@@ -0,0 +1 @@
1
+ juham_visualcrossing