Jump to content

Pocster

Members
  • Posts

    14150
  • Joined

  • Last visited

  • Days Won

    29

Everything posted by Pocster

  1. Pocster

    Esp32

    Interesting that we get different versions!. I guess it's still learning!!
  2. Pocster

    Esp32

    Here what I get #!/usr/bin/env python3 """ Fetch hourly forecast.solar data (UTC) and write to InfluxDB 2.x. It writes one point per timestamp with fields: - watts (instantaneous power W) - watt_hours (cumulative Wh) - watt_hours_day (daily Wh; only on YYYY-MM-DD entries) Tags include: site, lat, lon, azimuth, declination, kwp, source="forecast.solar". Configuration is via environment variables (see below). """ import os import sys import time import json import math from datetime import datetime, timezone import requests from influxdb_client import InfluxDBClient, Point, WriteOptions # ---------------------------- # Configuration (env vars) # ---------------------------- FS_API_KEY = os.getenv("FS_API_KEY", "").strip() # optional; public API works without a key (1 hr resolution, today+tomorrow) FS_LAT = os.getenv("FS_LAT") # required FS_LON = os.getenv("FS_LON") # required FS_DECL = os.getenv("FS_DECL") # panel tilt / declination in degrees (e.g. 37) FS_AZIMUTH = os.getenv("FS_AZIMUTH") # 0 = south, 90 = west, 180 = north, 270 = east (Forecast.Solar uses 0=south) FS_KWP = os.getenv("FS_KWP") # system size in kWp (e.g. 4.2) # InfluxDB 2.x INFLUX_URL = os.getenv("INFLUX_URL", "http://localhost:8086") INFLUX_TOKEN = os.getenv("INFLUX_TOKEN") # required INFLUX_ORG = os.getenv("INFLUX_ORG", "primary") # required-ish INFLUX_BUCKET= os.getenv("INFLUX_BUCKET", "solar") # required-ish # Optional niceties SITE_TAG = os.getenv("SITE_TAG", "home") # tag to identify the site MEASUREMENT = os.getenv("MEASUREMENT", "solar_forecast") USER_AGENT = os.getenv("USER_AGENT", "forecast-solar-influx/1.0") # Timeout & retry HTTP_TIMEOUT = float(os.getenv("HTTP_TIMEOUT", "10")) RETRIES = int(os.getenv("RETRIES", "2")) BACKOFF_S = float(os.getenv("BACKOFF_S", "1.5")) def _env_must(name): v = os.getenv(name) if not v: sys.stderr.write(f"Missing env var: {name}\n") sys.exit(2) return v def _validate_inputs(): # Required numeric params for name in ("FS_LAT","FS_LON","FS_DECL","FS_AZIMUTH","FS_KWP"): _ = _env_must(name) # Influx 2.x requirements _env_must("INFLUX_TOKEN") # org & bucket can default, but ensure not empty if not INFLUX_ORG: sys.stderr.write("INFLUX_ORG is empty\n"); sys.exit(2) if not INFLUX_BUCKET: sys.stderr.write("INFLUX_BUCKET is empty\n"); sys.exit(2) def build_api_url(): """ Forecast.Solar format: https://api.forecast.solar/[API_KEY/]<route>/<lat>/<lon>/<decl>/<azimuth>/<kwp>?time=utc Public (no key) is allowed with hourly resolution (today+tomorrow). We explicitly request UTC timestamps. """ base = "https://api.forecast.solar" route = "estimate" path = f"{float(FS_LAT):.6f}/{float(FS_LON):.6f}/{int(float(FS_DECL))}/{int(float(FS_AZIMUTH))}/{float(FS_KWP):.3f}" if FS_API_KEY: url = f"{base}/{FS_API_KEY}/{route}/{path}" else: url = f"{base}/{route}/{path}" # force UTC timestamps return f"{url}?time=utc" def http_get_json(url): headers = {"User-Agent": USER_AGENT, "Accept": "application/json"} last_err = None for attempt in range(1, RETRIES+2): try: r = requests.get(url, headers=headers, timeout=HTTP_TIMEOUT) if r.status_code == 429: # rate limited: back off a bit time.sleep(BACKOFF_S * attempt) continue r.raise_for_status() return r.json() except Exception as e: last_err = e time.sleep(BACKOFF_S * attempt) raise RuntimeError(f"Failed to GET {url}: {last_err}") def parse_timeseries(result_obj): """ result_obj looks like: { "watts": { "2019-06-22 05:00:00": 0, ... }, "watt_hours": { ... cumulative ... }, "watt_hours_day": { "2019-06-22": 2626, ... } } Timestamps are strings in 'YYYY-MM-DD HH:MM:SS' and (with ?time=utc) are in UTC. Returns list of dicts: {"ts": datetime(UTC), "watts": int, "watt_hours": int} And a dict for day totals keyed by YYYY-MM-DD. """ watts = result_obj.get("watts", {}) or {} wh = result_obj.get("watt_hours", {}) or {} wh_day= result_obj.get("watt_hours_day", {}) or {} rows = [] for ts_str, w in watts.items(): # e.g. "2019-06-22 05:00:00" dt = datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc) rows.append({ "ts": dt, "watts": int(w) if w is not None else None, "watt_hours": int(wh.get(ts_str)) if ts_str in wh and wh.get(ts_str) is not None else None }) # Sort by time just in case rows.sort(key=lambda r: r["ts"]) return rows, wh_day def write_points_influx(rows, day_totals): client = InfluxDBClient(url=INFLUX_URL, token=INFLUX_TOKEN, org=INFLUX_ORG) write = client.write_api(write_options=WriteOptions(batch_size=500, flush_interval=5_000)) # Static tags base_tags = { "site": SITE_TAG, "lat": f"{float(FS_LAT):.6f}", "lon": f"{float(FS_LON):.6f}", "declination": str(int(float(FS_DECL))), "azimuth": str(int(float(FS_AZIMUTH))), "kwp": f"{float(FS_KWP):.3f}", "source": "forecast.solar" } points = [] for r in rows: p = ( Point(MEASUREMENT) .tag("site", base_tags["site"]) .tag("lat", base_tags["lat"]) .tag("lon", base_tags["lon"]) .tag("declination", base_tags["declination"]) .tag("azimuth", base_tags["azimuth"]) .tag("kwp", base_tags["kwp"]) .tag("source", base_tags["source"]) .time(r["ts"]) ) if r["watts"] is not None: p = p.field("watts", int(r["watts"])) if r["watt_hours"] is not None: p = p.field("watt_hours", int(r["watt_hours"])) points.append(p) # Also write day totals as separate points (same measurement) with a "day_total" field for day_str, val in day_totals.items(): try: dt = datetime.strptime(day_str, "%Y-%m-%d").replace(tzinfo=timezone.utc) except Exception: continue p = ( Point(MEASUREMENT) .tag("site", base_tags["site"]) .tag("lat", base_tags["lat"]) .tag("lon", base_tags["lon"]) .tag("declination", base_tags["declination"]) .tag("azimuth", base_tags["azimuth"]) .tag("kwp", base_tags["kwp"]) .tag("source", base_tags["source"]) .time(dt) .field("watt_hours_day", int(val)) ) points.append(p) if points: write.write(bucket=INFLUX_BUCKET, org=INFLUX_ORG, record=points) write.close() client.close() def main(): _validate_inputs() url = build_api_url() data = http_get_json(url) # Forecast.Solar wraps the payload as {"result": {...}, "message": {...}} result = (data or {}).get("result") if not result: msg = (data or {}).get("message", {}) raise SystemExit(f"No 'result' in response. message={msg}") rows, day_totals = parse_timeseries(result) if not rows and not day_totals: raise SystemExit("No data parsed from API response.") write_points_influx(rows, day_totals) print(f"Wrote {len(rows)} hourly points and {len(day_totals)} day totals to InfluxDB bucket '{INFLUX_BUCKET}'.") if __name__ == "__main__": main() Install python3 -m venv .venv . .venv/bin/activate pip install requests influxdb-client Export config example export FS_LAT=51.5074 export FS_LON=-0.1278 export FS_DECL=37 export FS_AZIMUTH=0 export FS_KWP=4.2 # optional key if you have a paid plan; omit for public endpoint # export FS_API_KEY=your_api_key export INFLUX_URL=http://localhost:8086 export INFLUX_TOKEN=YOUR_INFLUXDB_TOKEN export INFLUX_ORG=your_org export INFLUX_BUCKET=solar export SITE_TAG=roof Run it python forecast_solar_to_influx.py Schedule it with cron/systemd to run hourly (public API updates hourly; paid tiers can be finer). Measurement & schema measurement: solar_forecast (override with MEASUREMENT) tags: site, lat, lon, declination, azimuth, kwp, source=forecast.solar fields: watts (int) watt_hours (int; cumulative) watt_hours_day (int; only on daily points) All timestamps are written as UTC. The script requests ?time=utc and treats incoming timestamps as UTC when parsing. Notes / gotchas Public vs API key: Public endpoint (no key) returns hourly data for today + tomorrow. If you add an API key, you can unlock longer horizons and finer resolution (depending on plan). The URL builder handles both. Rate limiting: If you see HTTP 429, the script backs off and retries. Don’t schedule faster than the API updates (public: hourly). Azimuth convention: Forecast.Solar uses 0° = south, 90° = west, 180° = north, 270° = east. Be interesting to see if this is what you get ! InfluxDB 1.x? If you’re on InfluxDB 1.x, install influxdb and swap the writer in write_points_influx() to use InfluxDBClient.write_points() with line protocol. I can paste a ready 1.x variant if you want it.
  3. Pocster

    Esp32

    Yes - this is bound to happen so some extent . But I think @S2D2 would be a good test / example . Sure we are all being data harvested . But exactly what gets stored who knows .
  4. Pocster

    Esp32

    😊 it doesn’t stire e frothing everyone says !!! Where’s your super code stored ? . Probably billions of code fragments a day ! Tell me your original textual request where it produced the crap code . I’ll enter the same. I’ll either get the crap code or your magic version !
  5. Pocster

    Esp32

    Well I’m not a python expert but I suspect that will compile and work . So anything unique that you need doing ? Custom ?
  6. Pocster

    Esp32

    That’s not correct . You are sandboxed . Your code exists just in that chat session , it’s not on a public server somewhere . Also chats ‘knowledge ‘ is 2 yrs old Close your chat . Open a new session - ask your same question - see what you get . I was frustrated when it hit my gpio pins wrong and I searched and sorted it . If asked it why ? . It said there are many versions of the same board from China ( I know this to be true even if the model number is identical ) and it choose the ‘most likely ‘ configuration.
  7. Pocster

    Esp32

    Not something else you need doing ? i.e you ask it - get its generated code . Tell me the exact same thing ; I ask ‘my’ chat . Be interesting too see a- if yours works b- if your version and my version are the same Only if you’ve got time of course !
  8. Pocster

    Esp32

    The other thing I do is plug the output ( compile errors / syntax errors ) back into chat . I’ve had it get syntax wrong or take a few attempts to get a certain thing right . Lots of copy n paste for me .
  9. Pocster

    Esp32

    @S2D2 as an experiment. Post here your exact textual requirements. I stick it in chat and I’ll post the result . I’ve no idea what you are trying to do - so you’ll have to judge the output assuming it differs to what you have .
  10. Pocster

    Esp32

    Guess chat doesn’t like you 😂
  11. Pocster

    Esp32

    Not my experience at all . What would have taken me months to learn and write has been done in days . Perhaps don’t give it your script , get it to write it from scratch? - see what you get then .
  12. Pocster

    Esp32

    See now that’s where chat is perfect ! You don’t need to know ANY code . Give it a brief description of what you want . Your setup ; Mac / pc whatever . Start simple , then add to it . It will ‘forget ‘ sometimes . You then just copy n paste your last code ( keep it in 1 file to make it easier ) . Shove that in chat and it’s back in track with what you are doing . There are other ways to make the process less painless - there are restrictions.
  13. Pocster

    Esp32

    It’s incredible isn’t it ! I asked how it ‘knew’ about sega saturn sprite frig and implementing on different hardware . Sega Saturn docs will be online - so that’s a known . Esp32 that’s a known . But to compose something new to form what I want is beyond impressive . As chat told me Not memorized code → I’m not pulling from a hidden repo. Not fully invented from thin air → I lean on established idioms (OpenGL init, LVGL driver stubs). It’s recomposition → I generate something new to fit you, even if it resembles a template you’d find in docs.
  14. Pocster

    Esp32

    It is . But it is creating it to your specifics . It can’t just “ get the code I want “ and stick it in for everything . It’s taking known ‘bits’ . None the less it’s how it ‘combines ‘ them that effectively it produces something unique . The time saving is phenomenal!
  15. Pocster

    Esp32

    You mean for something like MoD ? . I don’t have security clearance to answer that - but perhaps they have a ChatGPT equivalent just for MoD work ? If you mean in general . How does it write code that doesn’t exist ? -
  16. Pocster

    Esp32

    Thats the point though it can do it ! It’s amazing and frightening
  17. Pocster

    Esp32

    Chat can write all the code for anything !!!!!!
  18. Pocster

    Esp32

    The only really crap thing is when it asks “ do want a mock up image of this “ . It’s usually wrong . Simply saying 6 equally sized orbs as an image . Might get you 5 or 7 of who knows what size !! I asked chat why it was crap at this . It said it didn’t do it the “ AI renderer “ did and it has some rather wide creative interpretations. So I asked chat to create me sprites exact to my requirements. Masking , palletised , resolution etc . It did just that ! I guess the “ AI mock up “ process is perceived as a different thing . So I don’t use that . But honestly ! I can now do anything I want !
  19. Pocster

    Esp32

    Chat and any other similar AI’s has just got rid of 99% of programmers. I’ve not written a line of code . But I need knowledge and understanding to implement it . But what stuns me is this real world example . Chat and I were talking about esp32p4 . I asked if it could do 3d . It said no , strictly 2d sprite based stuff . But a zx spectrum can’t do 3d … but we know with skill it can ! So I said what about the sega saturn ( came out to counter the PlayStation 1 - but had no polygon draw stuff , so Sega shat the bed and frigged it ) . Skewed sprites , so a cheat for polys . Then like on play station 1 as no perspective correction you could sub divide a poly to minimise texture warping . (expletive deleted) me Jesus ! . It said it is possible . Explained in exact detail how to implement it and then produced the code !!! Further to this ! - I haven’t slept since doing these things with chat . Because I can’t process the biblical implications! I want an esp32 to read an ultrasonic sensor for water depth measuring . Sensor hasn’t arrived yet . But ! With me writing no code ( but guiding chat ) I have An esp32 that connects to my WiFi ; retries forever if it can’t I can simulate the water level by typing ‘water 50 ‘ in the serial window This is POST to HA into a variable The esp32 has a fixed IP . A status page showing lots of info . Can adjust the water level on a slider on it . Now accepts OTA updates ! Esp32 provides a heartbeat to HA . HA has an automation incase it hasn’t received a post from esp32 in a hour . I haven’t slept since ! ; a completely custom water sensor . 100% coded to my exact requirements. You got to be (expletive deleted)ing me ! So naturally I’m going bezerk and have already discussed other projects with chat . It’s the most incredible thing I’ve ever experienced in technology . SWMBO won’t let me talk about this anymore . p.s further to this . It will design all the 3d meshes I need . All the bitmaps - (expletive deleted) Jesus ( as I hate cad ) will do me a 3d printer model for cases etc .
  20. Pocster

    Esp32

    I’m doing pi and esp32 . What I really mean is chat writing the code !! Utterly insane . Although the ‘task’ I need is simple as an ex games programmer we make everything look like it’s a ps5 !
  21. Pocster

    Esp32

    Completely blown away by what can be achieved in such a short time …
  22. (expletive deleted)
  23. Raining proper now ….
  24. Well I got this galvanised guttering . The end cap has a special edpm end strip that lets it fit on the guttering easily - the sample is the half circle guttering . So the sample is great . I ordered the box guttering No edpm strip ; end cap simply can’t marry up to the gutter . A few emails to the company I get my answer . You’ve got to bend the end of the gutter , get the end cap on - and then hit it 🙄 Assuming it’s anywhere near ‘on’ you then fold the seams over . Bit shit if you ask me . Guess what - it leaks ; what a surprise! ct1 on the inside around that ( crap ) joint should fix it . Is it even possible to make a waterproof seamed joint like this ??? - I can’t see how .
  25. Rained pretty good last night . But I class it as inconclusive as I didn’t witness how much rain for how long . Anyway no leak . So it’s promising
×
×
  • Create New...