{"id":191736,"name":"GridWorks SpaceHeat SCADA","description":"Intended for running a heat pump thermal storage space heating system in a house, and doing this transactively.","url":"https://github.com/thegridelectric/gridworks-scada","last_synced_at":"2026-04-17T09:30:22.567Z","repository":{"id":36992925,"uuid":"492994913","full_name":"thegridelectric/gridworks-scada","owner":"thegridelectric","description":"GridWorks SCADA for space heating","archived":false,"fork":false,"pushed_at":"2026-04-04T01:31:07.000Z","size":6815,"stargazers_count":6,"open_issues_count":46,"forks_count":2,"subscribers_count":5,"default_branch":"dev","last_synced_at":"2026-04-04T03:02:52.857Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thegridelectric.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-05-16T20:48:04.000Z","updated_at":"2026-03-27T11:14:56.000Z","dependencies_parsed_at":"2026-03-26T23:02:35.796Z","dependency_job_id":null,"html_url":"https://github.com/thegridelectric/gridworks-scada","commit_stats":{"total_commits":966,"total_committers":6,"mean_commits":161.0,"dds":0.4782608695652174,"last_synced_commit":"4dad6afa246227d05e6df64438bcd25515656d25"},"previous_names":["thegridelectric/gridworks-scada","thegridelectric/gw-scada-spaceheat-python"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/thegridelectric/gridworks-scada","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thegridelectric","download_url":"https://codeload.github.com/thegridelectric/gridworks-scada/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31540826,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T16:28:08.000Z","status":"online","status_checked_at":"2026-04-08T02:00:06.127Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"owner":{"login":"thegridelectric","name":"GridWorks","uuid":"20042700","kind":"organization","description":"GridWorks is an agent-based, scalable and secure system for decarbonizing power grids.","email":"gridworks@gridworks-consulting.com","website":null,"location":null,"twitter":null,"company":null,"icon_url":"https://avatars.githubusercontent.com/u/20042700?v=4","repositories_count":7,"last_synced_at":"2023-03-03T23:15:06.930Z","metadata":{"has_sponsors_listing":false},"html_url":"https://github.com/thegridelectric","funding_links":[],"total_stars":null,"followers":null,"following":null,"created_at":"2022-11-14T06:28:03.880Z","updated_at":"2023-03-03T23:15:06.980Z","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thegridelectric","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thegridelectric/repositories"},"packages":[],"commits":{"id":9348063,"full_name":"thegridelectric/gridworks-scada","default_branch":"main","total_commits":2265,"total_committers":6,"total_bot_commits":0,"total_bot_committers":0,"mean_commits":377.5,"dds":0.5545253863134658,"past_year_total_commits":495,"past_year_total_committers":3,"past_year_total_bot_commits":0,"past_year_total_bot_committers":0,"past_year_mean_commits":165.0,"past_year_dds":0.5252525252525253,"last_synced_at":"2026-04-16T05:24:43.197Z","last_synced_commit":"71ae4e680b8e80e4261110303f102c83419d5ed1","created_at":"2025-03-26T22:32:01.595Z","updated_at":"2026-04-16T05:24:17.161Z","committers":[{"name":"Jessica Millar","email":"jessica.lynn.millar@gmail.com","login":"jessicamillar","count":1009},{"name":"thdfw","email":"th.defauw@gmail.com","login":"thdfw","count":661},{"name":"Andrew Schweitzer","email":"schweitz72@gmail.com","login":"anschweitzer","count":516},{"name":"stickler-ci","email":"support@stickler-ci.com","login":"stickler-ci","count":63},{"name":"Preethi Vaidyanathan","email":"preethi@gaia-scope.com","login":null,"count":15},{"name":"Jessica Millar","email":"jessica@jessica.local","login":null,"count":1}],"past_year_committers":[{"name":"thdfw","email":"th.defauw@gmail.com","login":"thdfw","count":235},{"name":"Jessica Millar","email":"jessica.lynn.millar@gmail.com","login":"jessicamillar","count":176},{"name":"Andrew Schweitzer","email":"schweitz72@gmail.com","login":"anschweitzer","count":84}],"commits_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada/commits","host":{"name":"GitHub","url":"https://github.com","kind":"github","last_synced_at":"2026-04-16T00:00:20.271Z","repositories_count":6213939,"commits_count":900243163,"contributors_count":34926135,"owners_count":1144827,"icon_url":"https://github.com/github.png","host_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://commits.ecosyste.ms/api/v1/hosts/GitHub/repositories"}},"issues_stats":{"full_name":"thegridelectric/gridworks-scada","html_url":"https://github.com/thegridelectric/gridworks-scada","last_synced_at":"2026-04-08T05:00:29.879Z","status":"active","issues_count":7,"pull_requests_count":139,"avg_time_to_close_issue":90635.5,"avg_time_to_close_pull_request":172081.75471698114,"issues_closed_count":2,"pull_requests_closed_count":106,"pull_request_authors_count":3,"issue_authors_count":2,"avg_comments_per_issue":0.5714285714285714,"avg_comments_per_pull_request":0.17266187050359713,"merged_pull_requests_count":92,"bot_issues_count":0,"bot_pull_requests_count":0,"past_year_issues_count":7,"past_year_pull_requests_count":111,"past_year_avg_time_to_close_issue":90635.5,"past_year_avg_time_to_close_pull_request":211703.71794871794,"past_year_issues_closed_count":2,"past_year_pull_requests_closed_count":78,"past_year_pull_request_authors_count":3,"past_year_issue_authors_count":2,"past_year_avg_comments_per_issue":0.5714285714285714,"past_year_avg_comments_per_pull_request":0.1981981981981982,"past_year_bot_issues_count":0,"past_year_bot_pull_requests_count":0,"past_year_merged_pull_requests_count":69,"created_at":"2025-03-26T22:32:02.177Z","updated_at":"2026-04-08T05:00:29.879Z","repository_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada","issues_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/thegridelectric%2Fgridworks-scada/issues","issue_labels_count":{"admin":3,"ops":3},"pull_request_labels_count":{"DRAFT PR":2,"admin":2,"ops":2},"issue_author_associations_count":{"COLLABORATOR":7},"pull_request_author_associations_count":{"COLLABORATOR":96,"CONTRIBUTOR":43},"issue_authors":{"anschweitzer":6,"jessicamillar":1},"pull_request_authors":{"jessicamillar":57,"thdfw":43,"anschweitzer":39},"host":{"name":"GitHub","url":"https://github.com","kind":"github","last_synced_at":"2026-04-16T00:00:09.014Z","repositories_count":14284363,"issues_count":34601114,"pull_requests_count":113220233,"authors_count":11235822,"icon_url":"https://github.com/github.png","host_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories","owners_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/owners","authors_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors"},"past_year_issue_labels_count":{"admin":3,"ops":3},"past_year_pull_request_labels_count":{"admin":2,"DRAFT PR":2,"ops":2},"past_year_issue_author_associations_count":{"COLLABORATOR":7},"past_year_pull_request_author_associations_count":{"COLLABORATOR":72,"CONTRIBUTOR":39},"past_year_issue_authors":{"anschweitzer":6,"jessicamillar":1},"past_year_pull_request_authors":{"jessicamillar":42,"thdfw":39,"anschweitzer":30},"maintainers":[{"login":"jessicamillar","count":58,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/jessicamillar"},{"login":"anschweitzer","count":45,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/anschweitzer"}],"active_maintainers":[{"login":"jessicamillar","count":43,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/jessicamillar"},{"login":"anschweitzer","count":36,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/anschweitzer"}]},"events":{"total":{"ReleaseEvent":14,"DeleteEvent":63,"PullRequestEvent":108,"IssuesEvent":6,"WatchEvent":1,"IssueCommentEvent":20,"PushEvent":529,"PullRequestReviewCommentEvent":4,"PullRequestReviewEvent":7,"CreateEvent":83},"last_year":{"ReleaseEvent":14,"DeleteEvent":61,"PullRequestEvent":105,"IssuesEvent":6,"WatchEvent":1,"IssueCommentEvent":20,"PushEvent":460,"PullRequestReviewCommentEvent":4,"PullRequestReviewEvent":7,"CreateEvent":80}},"keywords":[],"dependencies":[{"ecosystem":"pypi","filepath":"gw_spaceheat/requirements/base.txt","sha":null,"kind":"lockfile","created_at":"2022-07-08T07:43:33.596Z","updated_at":"2022-07-08T07:43:33.596Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/gw_spaceheat/requirements/base.txt","dependencies":[{"id":119882284,"package_name":"numpy","ecosystem":"pypi","requirements":"==1.22.4","direct":false,"kind":"runtime","optional":false},{"id":119882287,"package_name":"paho-mqtt","ecosystem":"pypi","requirements":"==1.6.1","direct":false,"kind":"runtime","optional":false},{"id":119882289,"package_name":"pendulum","ecosystem":"pypi","requirements":"==2.1.2","direct":false,"kind":"runtime","optional":false},{"id":119882290,"package_name":"pika","ecosystem":"pypi","requirements":"==1.1.0","direct":false,"kind":"runtime","optional":false},{"id":119882292,"package_name":"python-dateutil","ecosystem":"pypi","requirements":"==2.8.2","direct":false,"kind":"runtime","optional":false},{"id":119882294,"package_name":"python-dotenv","ecosystem":"pypi","requirements":"==0.20.0","direct":false,"kind":"runtime","optional":false},{"id":119882295,"package_name":"pytz","ecosystem":"pypi","requirements":"==2022.1","direct":false,"kind":"runtime","optional":false},{"id":119882297,"package_name":"pytzdata","ecosystem":"pypi","requirements":"==2020.1","direct":false,"kind":"runtime","optional":false},{"id":119882299,"package_name":"six","ecosystem":"pypi","requirements":"==1.16.0","direct":false,"kind":"runtime","optional":false}]},{"ecosystem":"pypi","filepath":"gw_spaceheat/requirements/drivers.txt","sha":null,"kind":"lockfile","created_at":"2022-07-08T07:43:33.781Z","updated_at":"2022-07-08T07:43:33.781Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/gw_spaceheat/requirements/drivers.txt","dependencies":[{"id":119882654,"package_name":"numpy","ecosystem":"pypi","requirements":"==1.22.4","direct":false,"kind":"runtime","optional":false},{"id":119882655,"package_name":"paho-mqtt","ecosystem":"pypi","requirements":"==1.6.1","direct":false,"kind":"runtime","optional":false},{"id":119882656,"package_name":"pendulum","ecosystem":"pypi","requirements":"==2.1.2","direct":false,"kind":"runtime","optional":false},{"id":119882657,"package_name":"pika","ecosystem":"pypi","requirements":"==1.1.0","direct":false,"kind":"runtime","optional":false},{"id":119882658,"package_name":"pymodbus","ecosystem":"pypi","requirements":"==2.5.3","direct":false,"kind":"runtime","optional":false},{"id":119882659,"package_name":"pyserial","ecosystem":"pypi","requirements":"==3.5","direct":false,"kind":"runtime","optional":false},{"id":119882660,"package_name":"python-dateutil","ecosystem":"pypi","requirements":"==2.8.2","direct":false,"kind":"runtime","optional":false},{"id":119882661,"package_name":"python-dotenv","ecosystem":"pypi","requirements":"==0.20.0","direct":false,"kind":"runtime","optional":false},{"id":119882662,"package_name":"pytz","ecosystem":"pypi","requirements":"==2022.1","direct":false,"kind":"runtime","optional":false},{"id":119882663,"package_name":"pytzdata","ecosystem":"pypi","requirements":"==2020.1","direct":false,"kind":"runtime","optional":false},{"id":119882664,"package_name":"six","ecosystem":"pypi","requirements":"==1.16.0","direct":false,"kind":"runtime","optional":false},{"id":119882665,"package_name":"smbus2","ecosystem":"pypi","requirements":"==0.4.1","direct":false,"kind":"runtime","optional":false}]},{"ecosystem":"actions","filepath":".github/workflows/ci.yaml","sha":null,"kind":"manifest","created_at":"2023-01-17T12:00:58.912Z","updated_at":"2023-01-17T12:00:58.912Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/.github/workflows/ci.yaml","dependencies":[{"id":6988078763,"package_name":"actions/checkout","ecosystem":"actions","requirements":"v2","direct":true,"kind":"composite","optional":false},{"id":6988078764,"package_name":"namoshek/mosquitto-github-action","ecosystem":"actions","requirements":"v1","direct":true,"kind":"composite","optional":false},{"id":6988078765,"package_name":"actions/setup-python","ecosystem":"actions","requirements":"v2","direct":true,"kind":"composite","optional":false},{"id":6988078766,"package_name":"canastro/copy-file-action","ecosystem":"actions","requirements":"master","direct":true,"kind":"composite","optional":false},{"id":6988078767,"package_name":"actions/upload-artifact","ecosystem":"actions","requirements":"v3","direct":true,"kind":"composite","optional":false},{"id":6988078768,"package_name":"actions/download-artifact","ecosystem":"actions","requirements":"v2","direct":true,"kind":"composite","optional":false},{"id":6988078769,"package_name":"codecov/codecov-action","ecosystem":"actions","requirements":"v3.1.0","direct":true,"kind":"composite","optional":false}]},{"ecosystem":"pypi","filepath":"gw_spaceheat/requirements/dev.txt","sha":null,"kind":"manifest","created_at":"2023-01-17T12:00:59.037Z","updated_at":"2023-01-17T12:00:59.037Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/gw_spaceheat/requirements/dev.txt","dependencies":[{"id":6988079984,"package_name":"appnope","ecosystem":"pypi","requirements":"==0.1.3","direct":true,"kind":"development","optional":false},{"id":6988079988,"package_name":"asttokens","ecosystem":"pypi","requirements":"==2.0.5","direct":true,"kind":"development","optional":false},{"id":6988079991,"package_name":"attrs","ecosystem":"pypi","requirements":"==21.4.0","direct":true,"kind":"development","optional":false},{"id":6988079994,"package_name":"backcall","ecosystem":"pypi","requirements":"==0.2.0","direct":true,"kind":"development","optional":false},{"id":6988079997,"package_name":"black","ecosystem":"pypi","requirements":"==22.3.0","direct":true,"kind":"development","optional":false},{"id":6988080000,"package_name":"build","ecosystem":"pypi","requirements":"==0.8.0","direct":true,"kind":"development","optional":false},{"id":6988080004,"package_name":"certifi","ecosystem":"pypi","requirements":"==2022.9.14","direct":true,"kind":"development","optional":false},{"id":6988080008,"package_name":"charset-normalizer","ecosystem":"pypi","requirements":"==2.1.1","direct":true,"kind":"development","optional":false},{"id":6988080011,"package_name":"click","ecosystem":"pypi","requirements":"==8.1.3","direct":true,"kind":"development","optional":false},{"id":6988080014,"package_name":"commonmark","ecosystem":"pypi","requirements":"==0.9.1","direct":true,"kind":"development","optional":false},{"id":6988080017,"package_name":"coverage","ecosystem":"pypi","requirements":"==6.4.1","direct":true,"kind":"development","optional":false},{"id":6988080020,"package_name":"decorator","ecosystem":"pypi","requirements":"==5.1.1","direct":true,"kind":"development","optional":false},{"id":6988080024,"package_name":"docutils","ecosystem":"pypi","requirements":"==0.19","direct":true,"kind":"development","optional":false},{"id":6988080026,"package_name":"executing","ecosystem":"pypi","requirements":"==0.8.3","direct":true,"kind":"development","optional":false},{"id":6988080029,"package_name":"gridworks-protocol","ecosystem":"pypi","requirements":"==0.2.2","direct":true,"kind":"development","optional":false},{"id":6988080032,"package_name":"idna","ecosystem":"pypi","requirements":"==3.4","direct":true,"kind":"development","optional":false},{"id":6988080035,"package_name":"iniconfig","ecosystem":"pypi","requirements":"==1.1.1","direct":true,"kind":"development","optional":false},{"id":6988080038,"package_name":"ipython","ecosystem":"pypi","requirements":"==8.0.0","direct":true,"kind":"development","optional":false},{"id":6988080040,"package_name":"isort","ecosystem":"pypi","requirements":"==5.10.1","direct":true,"kind":"development","optional":false},{"id":6988080043,"package_name":"jedi","ecosystem":"pypi","requirements":"==0.18.1","direct":true,"kind":"development","optional":false},{"id":6988080046,"package_name":"matplotlib-inline","ecosystem":"pypi","requirements":"==0.1.3","direct":true,"kind":"development","optional":false},{"id":6988080049,"package_name":"mypy","ecosystem":"pypi","requirements":"==0.961","direct":true,"kind":"development","optional":false},{"id":6988080051,"package_name":"mypy-extensions","ecosystem":"pypi","requirements":"==0.4.3","direct":true,"kind":"development","optional":false},{"id":6988080054,"package_name":"nose","ecosystem":"pypi","requirements":"==1.3.7","direct":true,"kind":"development","optional":false},{"id":6988080092,"package_name":"numpy","ecosystem":"pypi","requirements":"==1.22.4","direct":true,"kind":"development","optional":false},{"id":6988080097,"package_name":"packaging","ecosystem":"pypi","requirements":"==21.3","direct":true,"kind":"development","optional":false},{"id":6988080098,"package_name":"paho-mqtt","ecosystem":"pypi","requirements":"==1.6.1","direct":true,"kind":"development","optional":false},{"id":6988080101,"package_name":"parso","ecosystem":"pypi","requirements":"==0.8.3","direct":true,"kind":"development","optional":false},{"id":6988080102,"package_name":"pathspec","ecosystem":"pypi","requirements":"==0.9.0","direct":true,"kind":"development","optional":false},{"id":6988080105,"package_name":"pendulum","ecosystem":"pypi","requirements":"==2.1.2","direct":true,"kind":"development","optional":false},{"id":6988080218,"package_name":"pep517","ecosystem":"pypi","requirements":"==0.12.0","direct":true,"kind":"development","optional":false},{"id":6988080225,"package_name":"pexpect","ecosystem":"pypi","requirements":"==4.8.0","direct":true,"kind":"development","optional":false},{"id":6988080227,"package_name":"pickleshare","ecosystem":"pypi","requirements":"==0.7.5","direct":true,"kind":"development","optional":false},{"id":6988080230,"package_name":"pika","ecosystem":"pypi","requirements":"==1.1.0","direct":true,"kind":"development","optional":false},{"id":6988080232,"package_name":"pip-tools","ecosystem":"pypi","requirements":"==6.9.0","direct":true,"kind":"development","optional":false},{"id":6988080235,"package_name":"platformdirs","ecosystem":"pypi","requirements":"==2.5.2","direct":true,"kind":"development","optional":false},{"id":6988080237,"package_name":"pluggy","ecosystem":"pypi","requirements":"==1.0.0","direct":true,"kind":"development","optional":false},{"id":6988080239,"package_name":"prompt-toolkit","ecosystem":"pypi","requirements":"==3.0.29","direct":true,"kind":"development","optional":false},{"id":6988080241,"package_name":"ptyprocess","ecosystem":"pypi","requirements":"==0.7.0","direct":true,"kind":"development","optional":false},{"id":6988080243,"package_name":"pure-eval","ecosystem":"pypi","requirements":"==0.2.2","direct":true,"kind":"development","optional":false},{"id":6988080245,"package_name":"py","ecosystem":"pypi","requirements":"==1.11.0","direct":true,"kind":"development","optional":false},{"id":6988080247,"package_name":"pydantic","ecosystem":"pypi","requirements":"==1.10.2","direct":true,"kind":"development","optional":false},{"id":6988080249,"package_name":"pygments","ecosystem":"pypi","requirements":"==2.12.0","direct":true,"kind":"development","optional":false},{"id":6988080251,"package_name":"pymodbus","ecosystem":"pypi","requirements":"==2.5.3","direct":true,"kind":"development","optional":false},{"id":6988080262,"package_name":"pyparsing","ecosystem":"pypi","requirements":"==3.0.9","direct":true,"kind":"development","optional":false},{"id":6988080264,"package_name":"pyserial","ecosystem":"pypi","requirements":"==3.5","direct":true,"kind":"development","optional":false},{"id":6988080267,"package_name":"pytest","ecosystem":"pypi","requirements":"==7.1.2","direct":true,"kind":"development","optional":false},{"id":6988080269,"package_name":"pytest-asyncio","ecosystem":"pypi","requirements":"==0.19.0","direct":true,"kind":"development","optional":false},{"id":6988080271,"package_name":"pytest-sugar","ecosystem":"pypi","requirements":"==0.9.5","direct":true,"kind":"development","optional":false},{"id":6988080276,"package_name":"python-dateutil","ecosystem":"pypi","requirements":"==2.8.2","direct":true,"kind":"development","optional":false},{"id":6988080278,"package_name":"python-dotenv","ecosystem":"pypi","requirements":"==0.20.0","direct":true,"kind":"development","optional":false},{"id":6988080280,"package_name":"pytz","ecosystem":"pypi","requirements":"==2022.1","direct":true,"kind":"development","optional":false},{"id":6988080282,"package_name":"pytzdata","ecosystem":"pypi","requirements":"==2020.1","direct":true,"kind":"development","optional":false},{"id":6988080284,"package_name":"requests","ecosystem":"pypi","requirements":"==2.28.1","direct":true,"kind":"development","optional":false},{"id":6988080287,"package_name":"result","ecosystem":"pypi","requirements":"==0.8.0","direct":true,"kind":"development","optional":false},{"id":6988080324,"package_name":"rich","ecosystem":"pypi","requirements":"==12.5.1","direct":true,"kind":"development","optional":false},{"id":6988080326,"package_name":"rich-cli","ecosystem":"pypi","requirements":"==1.8.0","direct":true,"kind":"development","optional":false},{"id":6988080356,"package_name":"rich-rst","ecosystem":"pypi","requirements":"==1.1.7","direct":true,"kind":"development","optional":false},{"id":6988080376,"package_name":"six","ecosystem":"pypi","requirements":"==1.16.0","direct":true,"kind":"development","optional":false},{"id":6988080380,"package_name":"smbus2","ecosystem":"pypi","requirements":"==0.4.1","direct":true,"kind":"development","optional":false},{"id":6988080479,"package_name":"stack-data","ecosystem":"pypi","requirements":"==0.2.0","direct":true,"kind":"development","optional":false},{"id":6988080483,"package_name":"termcolor","ecosystem":"pypi","requirements":"==1.1.0","direct":true,"kind":"development","optional":false},{"id":6988080486,"package_name":"textual","ecosystem":"pypi","requirements":"==0.1.18","direct":true,"kind":"development","optional":false},{"id":6988080490,"package_name":"tomli","ecosystem":"pypi","requirements":"==2.0.1","direct":true,"kind":"development","optional":false},{"id":6988080493,"package_name":"traitlets","ecosystem":"pypi","requirements":"==5.1.1","direct":true,"kind":"development","optional":false},{"id":6988080495,"package_name":"typing-extensions","ecosystem":"pypi","requirements":"==4.2.0","direct":true,"kind":"development","optional":false},{"id":6988080499,"package_name":"urllib3","ecosystem":"pypi","requirements":"==1.26.12","direct":true,"kind":"development","optional":false},{"id":6988080502,"package_name":"wcwidth","ecosystem":"pypi","requirements":"==0.2.5","direct":true,"kind":"development","optional":false},{"id":6988080505,"package_name":"wheel","ecosystem":"pypi","requirements":"==0.37.1","direct":true,"kind":"development","optional":false},{"id":6988080508,"package_name":"xdg","ecosystem":"pypi","requirements":"==5.1.1","direct":true,"kind":"development","optional":false}]},{"ecosystem":"pypi","filepath":"gw_spaceheat/requirements/test.txt","sha":null,"kind":"manifest","created_at":"2023-01-17T12:00:59.175Z","updated_at":"2023-01-17T12:00:59.175Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/gw_spaceheat/requirements/test.txt","dependencies":[{"id":6988081923,"package_name":"attrs","ecosystem":"pypi","requirements":"==21.4.0","direct":true,"kind":"test","optional":false},{"id":6988081924,"package_name":"black","ecosystem":"pypi","requirements":"==22.3.0","direct":true,"kind":"test","optional":false},{"id":6988081925,"package_name":"click","ecosystem":"pypi","requirements":"==8.1.3","direct":true,"kind":"test","optional":false},{"id":6988081926,"package_name":"coverage","ecosystem":"pypi","requirements":"==6.4.1","direct":true,"kind":"test","optional":false},{"id":6988081927,"package_name":"iniconfig","ecosystem":"pypi","requirements":"==1.1.1","direct":true,"kind":"test","optional":false},{"id":6988081928,"package_name":"mypy","ecosystem":"pypi","requirements":"==0.961","direct":true,"kind":"test","optional":false},{"id":6988081929,"package_name":"mypy-extensions","ecosystem":"pypi","requirements":"==0.4.3","direct":true,"kind":"test","optional":false},{"id":6988081930,"package_name":"nose","ecosystem":"pypi","requirements":"==1.3.7","direct":true,"kind":"test","optional":false},{"id":6988081931,"package_name":"packaging","ecosystem":"pypi","requirements":"==21.3","direct":true,"kind":"test","optional":false},{"id":6988081932,"package_name":"pathspec","ecosystem":"pypi","requirements":"==0.9.0","direct":true,"kind":"test","optional":false},{"id":6988081933,"package_name":"platformdirs","ecosystem":"pypi","requirements":"==2.5.2","direct":true,"kind":"test","optional":false},{"id":6988081934,"package_name":"pluggy","ecosystem":"pypi","requirements":"==1.0.0","direct":true,"kind":"test","optional":false},{"id":6988081935,"package_name":"py","ecosystem":"pypi","requirements":"==1.11.0","direct":true,"kind":"test","optional":false},{"id":6988081936,"package_name":"pyparsing","ecosystem":"pypi","requirements":"==3.0.9","direct":true,"kind":"test","optional":false},{"id":6988081937,"package_name":"pytest","ecosystem":"pypi","requirements":"==7.1.2","direct":true,"kind":"test","optional":false},{"id":6988081938,"package_name":"pytest-asyncio","ecosystem":"pypi","requirements":"==0.19.0","direct":true,"kind":"test","optional":false},{"id":6988081939,"package_name":"tomli","ecosystem":"pypi","requirements":"==2.0.1","direct":true,"kind":"test","optional":false},{"id":6988081940,"package_name":"typing-extensions","ecosystem":"pypi","requirements":"==4.2.0","direct":true,"kind":"test","optional":false}]},{"ecosystem":"actions","filepath":".github/workflows/release.yml","sha":null,"kind":"manifest","created_at":"2026-01-20T13:07:03.727Z","updated_at":"2026-01-20T13:07:03.727Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/.github/workflows/release.yml","dependencies":[{"id":25800545819,"package_name":"actions/checkout","ecosystem":"actions","requirements":"v4","direct":true,"kind":"composite","optional":false},{"id":25800545820,"package_name":"actions/setup-python","ecosystem":"actions","requirements":"v5.6.0","direct":true,"kind":"composite","optional":false},{"id":25800545821,"package_name":"astral-sh/setup-uv","ecosystem":"actions","requirements":"v6","direct":true,"kind":"composite","optional":false},{"id":25800545822,"package_name":"salsify/action-detect-and-tag-new-version","ecosystem":"actions","requirements":"v2.0.3","direct":true,"kind":"composite","optional":false},{"id":25800545823,"package_name":"release-drafter/release-drafter","ecosystem":"actions","requirements":"v6.0.0","direct":true,"kind":"composite","optional":false}]},{"ecosystem":"pypi","filepath":"packages/gridworks-admin/pyproject.toml","sha":null,"kind":"manifest","created_at":"2026-01-20T13:07:04.046Z","updated_at":"2026-01-20T13:07:04.046Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/packages/gridworks-admin/pyproject.toml","dependencies":[{"id":25800545826,"package_name":"gridworks-proactor","ecosystem":"pypi","requirements":"\u003e=4.1.9","direct":true,"kind":"runtime","optional":false},{"id":25800545828,"package_name":"gridworks-scada-protocol","ecosystem":"pypi","requirements":"\u003e=1.2.0","direct":true,"kind":"runtime","optional":false},{"id":25800545829,"package_name":"numpy","ecosystem":"pypi","requirements":"\u003e=2.3.3","direct":true,"kind":"runtime","optional":false},{"id":25800545830,"package_name":"paho-mqtt","ecosystem":"pypi","requirements":"\u003e=2.1.0","direct":true,"kind":"runtime","optional":false},{"id":25800545832,"package_name":"pydantic","ecosystem":"pypi","requirements":"\u003e=2.11.9","direct":true,"kind":"runtime","optional":false},{"id":25800545833,"package_name":"python-dotenv","ecosystem":"pypi","requirements":"\u003e=1.1.1","direct":true,"kind":"runtime","optional":false},{"id":25800545834,"package_name":"result","ecosystem":"pypi","requirements":"\u003e=0.9.0","direct":true,"kind":"runtime","optional":false},{"id":25800545835,"package_name":"rich","ecosystem":"pypi","requirements":"\u003e=14.1.0","direct":true,"kind":"runtime","optional":false},{"id":25800545836,"package_name":"textual","ecosystem":"pypi","requirements":"\u003e=6.1.0","direct":true,"kind":"runtime","optional":false},{"id":25800545839,"package_name":"typer","ecosystem":"pypi","requirements":"\u003e=0.17.4","direct":true,"kind":"runtime","optional":false}]},{"ecosystem":"pypi","filepath":"packages/gridworks-scada-protocol/pyproject.toml","sha":null,"kind":"manifest","created_at":"2026-01-20T13:07:05.005Z","updated_at":"2026-01-20T13:07:05.005Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/packages/gridworks-scada-protocol/pyproject.toml","dependencies":[{"id":25800545894,"package_name":"gridworks-protocol","ecosystem":"pypi","requirements":"\u003e=1.3.3,\u003c2.0.0","direct":true,"kind":"runtime","optional":false},{"id":25800545896,"package_name":"transitions","ecosystem":"pypi","requirements":"\u003e=0.9.3","direct":true,"kind":"runtime","optional":false}]},{"ecosystem":"pypi","filepath":"packages/gridworks-admin/uv.lock","sha":null,"kind":"lockfile","created_at":"2026-01-20T13:07:04.665Z","updated_at":"2026-01-20T13:07:04.665Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/packages/gridworks-admin/uv.lock","dependencies":[{"id":25800545843,"package_name":"aiohappyeyeballs","ecosystem":"pypi","requirements":"2.6.1","direct":false,"kind":"runtime","optional":false},{"id":25800545845,"package_name":"aiohttp","ecosystem":"pypi","requirements":"3.12.15","direct":false,"kind":"runtime","optional":false},{"id":25800545846,"package_name":"aiosignal","ecosystem":"pypi","requirements":"1.4.0","direct":false,"kind":"runtime","optional":false},{"id":25800545847,"package_name":"annotated-types","ecosystem":"pypi","requirements":"0.7.0","direct":false,"kind":"runtime","optional":false},{"id":25800545848,"package_name":"attrs","ecosystem":"pypi","requirements":"25.3.0","direct":false,"kind":"runtime","optional":false},{"id":25800545849,"package_name":"click","ecosystem":"pypi","requirements":"8.2.1","direct":false,"kind":"runtime","optional":false},{"id":25800545850,"package_name":"colorama","ecosystem":"pypi","requirements":"0.4.6","direct":false,"kind":"runtime","optional":false},{"id":25800545851,"package_name":"frozenlist","ecosystem":"pypi","requirements":"1.7.0","direct":false,"kind":"runtime","optional":false},{"id":25800545852,"package_name":"gridworks","ecosystem":"pypi","requirements":"1.5.6","direct":false,"kind":"runtime","optional":false},{"id":25800545853,"package_name":"gridworks-admin","ecosystem":"pypi","requirements":"1.0.8","direct":false,"kind":"runtime","optional":false},{"id":25800545854,"package_name":"gridworks-proactor","ecosystem":"pypi","requirements":"4.1.9","direct":false,"kind":"runtime","optional":false},{"id":25800545855,"package_name":"gridworks-protocol","ecosystem":"pypi","requirements":"1.3.3","direct":false,"kind":"runtime","optional":false},{"id":25800545856,"package_name":"gridworks-scada-protocol","ecosystem":"pypi","requirements":"1.1.1","direct":false,"kind":"runtime","optional":false},{"id":25800545857,"package_name":"idna","ecosystem":"pypi","requirements":"3.10","direct":false,"kind":"runtime","optional":false},{"id":25800545858,"package_name":"linkify-it-py","ecosystem":"pypi","requirements":"2.0.3","direct":false,"kind":"runtime","optional":false},{"id":25800545859,"package_name":"markdown-it-py","ecosystem":"pypi","requirements":"4.0.0","direct":false,"kind":"runtime","optional":false},{"id":25800545860,"package_name":"mdit-py-plugins","ecosystem":"pypi","requirements":"0.5.0","direct":false,"kind":"runtime","optional":false},{"id":25800545861,"package_name":"mdurl","ecosystem":"pypi","requirements":"0.1.2","direct":false,"kind":"runtime","optional":false},{"id":25800545862,"package_name":"multidict","ecosystem":"pypi","requirements":"6.6.4","direct":false,"kind":"runtime","optional":false},{"id":25800545863,"package_name":"numpy","ecosystem":"pypi","requirements":"2.3.3","direct":false,"kind":"runtime","optional":false},{"id":25800545864,"package_name":"paho-mqtt","ecosystem":"pypi","requirements":"2.1.0","direct":false,"kind":"runtime","optional":false},{"id":25800545865,"package_name":"platformdirs","ecosystem":"pypi","requirements":"4.4.0","direct":false,"kind":"runtime","optional":false},{"id":25800545866,"package_name":"propcache","ecosystem":"pypi","requirements":"0.3.2","direct":false,"kind":"runtime","optional":false},{"id":25800545867,"package_name":"pydantic","ecosystem":"pypi","requirements":"2.11.9","direct":false,"kind":"runtime","optional":false},{"id":25800545868,"package_name":"pydantic-core","ecosystem":"pypi","requirements":"2.33.2","direct":false,"kind":"runtime","optional":false},{"id":25800545869,"package_name":"pydantic-extra-types","ecosystem":"pypi","requirements":"2.10.5","direct":false,"kind":"runtime","optional":false},{"id":25800545871,"package_name":"pydantic-settings","ecosystem":"pypi","requirements":"2.10.1","direct":false,"kind":"runtime","optional":false},{"id":25800545872,"package_name":"pygments","ecosystem":"pypi","requirements":"2.19.2","direct":false,"kind":"runtime","optional":false},{"id":25800545873,"package_name":"python-dotenv","ecosystem":"pypi","requirements":"1.1.1","direct":false,"kind":"runtime","optional":false},{"id":25800545874,"package_name":"pytz","ecosystem":"pypi","requirements":"2024.2","direct":false,"kind":"runtime","optional":false},{"id":25800545875,"package_name":"result","ecosystem":"pypi","requirements":"0.9.0","direct":false,"kind":"runtime","optional":false},{"id":25800545876,"package_name":"rich","ecosystem":"pypi","requirements":"14.1.0","direct":false,"kind":"runtime","optional":false},{"id":25800545877,"package_name":"shellingham","ecosystem":"pypi","requirements":"1.5.4","direct":false,"kind":"runtime","optional":false},{"id":25800545878,"package_name":"six","ecosystem":"pypi","requirements":"1.17.0","direct":false,"kind":"runtime","optional":false},{"id":25800545879,"package_name":"textual","ecosystem":"pypi","requirements":"6.1.0","direct":false,"kind":"runtime","optional":false},{"id":25800545880,"package_name":"transitions","ecosystem":"pypi","requirements":"0.9.3","direct":false,"kind":"runtime","optional":false},{"id":25800545881,"package_name":"typer","ecosystem":"pypi","requirements":"0.17.4","direct":false,"kind":"runtime","optional":false},{"id":25800545882,"package_name":"typing-extensions","ecosystem":"pypi","requirements":"4.15.0","direct":false,"kind":"runtime","optional":false},{"id":25800545883,"package_name":"typing-inspection","ecosystem":"pypi","requirements":"0.4.1","direct":false,"kind":"runtime","optional":false},{"id":25800545884,"package_name":"uc-micro-py","ecosystem":"pypi","requirements":"1.0.3","direct":false,"kind":"runtime","optional":false},{"id":25800545889,"package_name":"xdg","ecosystem":"pypi","requirements":"6.0.0","direct":false,"kind":"runtime","optional":false},{"id":25800545891,"package_name":"yarl","ecosystem":"pypi","requirements":"1.20.1","direct":false,"kind":"runtime","optional":false}]},{"ecosystem":"pypi","filepath":"packages/gridworks-scada-protocol/uv.lock","sha":null,"kind":"lockfile","created_at":"2026-01-20T13:07:05.157Z","updated_at":"2026-01-20T13:07:05.157Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/packages/gridworks-scada-protocol/uv.lock","dependencies":[{"id":25800545901,"package_name":"idna","ecosystem":"pypi","requirements":"3.10","direct":false,"kind":"runtime","optional":false},{"id":25800545902,"package_name":"multidict","ecosystem":"pypi","requirements":"6.6.4","direct":false,"kind":"runtime","optional":false},{"id":25800545908,"package_name":"propcache","ecosystem":"pypi","requirements":"0.3.2","direct":false,"kind":"runtime","optional":false},{"id":25800545909,"package_name":"pydantic","ecosystem":"pypi","requirements":"2.11.9","direct":false,"kind":"runtime","optional":false},{"id":25800545910,"package_name":"pydantic-core","ecosystem":"pypi","requirements":"2.33.2","direct":false,"kind":"runtime","optional":false},{"id":25800545911,"package_name":"pydantic-extra-types","ecosystem":"pypi","requirements":"2.10.5","direct":false,"kind":"runtime","optional":false},{"id":25800545912,"package_name":"python-dotenv","ecosystem":"pypi","requirements":"1.1.1","direct":false,"kind":"runtime","optional":false},{"id":25800545913,"package_name":"pytz","ecosystem":"pypi","requirements":"2024.2","direct":false,"kind":"runtime","optional":false},{"id":25800545914,"package_name":"six","ecosystem":"pypi","requirements":"1.17.0","direct":false,"kind":"runtime","optional":false},{"id":25800545915,"package_name":"transitions","ecosystem":"pypi","requirements":"0.9.3","direct":false,"kind":"runtime","optional":false},{"id":25800545916,"package_name":"typing-extensions","ecosystem":"pypi","requirements":"4.15.0","direct":false,"kind":"runtime","optional":false},{"id":25800545917,"package_name":"typing-inspection","ecosystem":"pypi","requirements":"0.4.1","direct":false,"kind":"runtime","optional":false},{"id":25800545918,"package_name":"yarl","ecosystem":"pypi","requirements":"1.20.1","direct":false,"kind":"runtime","optional":false},{"id":25800545897,"package_name":"annotated-types","ecosystem":"pypi","requirements":"0.7.0","direct":false,"kind":"runtime","optional":false},{"id":25800545898,"package_name":"gridworks","ecosystem":"pypi","requirements":"1.5.6","direct":false,"kind":"runtime","optional":false},{"id":25800545899,"package_name":"gridworks-protocol","ecosystem":"pypi","requirements":"1.3.0","direct":false,"kind":"runtime","optional":false},{"id":25800545900,"package_name":"gridworks-scada-protocol","ecosystem":"pypi","requirements":"1.0.1","direct":false,"kind":"runtime","optional":false}]},{"ecosystem":"pypi","filepath":"pyproject.toml","sha":null,"kind":"manifest","created_at":"2024-01-03T20:25:12.864Z","updated_at":"2024-01-03T20:25:12.864Z","repository_link":"https://github.com/thegridelectric/gridworks-scada/blob/dev/pyproject.toml","dependencies":[]}],"score":5.7430031878094825,"created_at":"2024-04-26T00:04:14.207Z","updated_at":"2026-04-17T09:30:22.581Z","avatar_url":"https://github.com/thegridelectric.png","language":"Python","category":"Consumption","sub_category":"Buildings and Heating","monthly_downloads":0,"total_dependent_repos":0,"total_dependent_packages":0,"readme":"# GridWorks SCADA\n\n[![Tests](https://github.com/thegridelectric/gridworks-scada/actions/workflows/ci.yaml/badge.svg)][tests]\n[![Codecov](https://codecov.io/gh/thegridelectric/gridworks-scada/branch/main/graph/badge.svg)][codecov]\n\n[tests]: https://github.com/thegridelectric/gridworks-scada/actions/workflows/ci.yaml\n[codecov]: https://app.codecov.io/gh/thegridelectric/gridworks-scada\n========\n\n\nThis code is intended for running a heat pump thermal storage space heating system in a house, and doing this _transactively_. That means the heating system is capable of dynamically responding to local electric grid conditions and buying energy at the lowest cost times, while keeping the house warm. We believe this repo may be instrumental in effectively and efficiently reaching a low-carbon future. For an architectural overview of the code, and why it has something to do with a low-carbon future, please go [here](docs/architecture-overview.md).\n\nThis code is part of a larger framework. In particular it assumes there is a cloud-based actor which it refers to as the [AtomicTNode](docs/atomic-t-node.md) (short for Atomic Transactive Node) that is calling the shots on its control decisions most of the time. In addition, the code is structured in an\nactor-based way, with a  collection of actors each responsible for an important but limited set\nof functionality communicating to each other via messages. For a more specific description of both how these internal actors work with each other and how \nthis repo fits into the larger transactive energy framework please go [here](docs/core-protocol-sequences.md); this page describes typical sequences of messages between relevant actors in the system.\n\n This project is funded by Efficiency Maine who spearheaded and funded the [initial pilot](docs/maine-heat-pilot.md) using this code. As per the requirements of the initial pilot, the code is intended to:\n  1) run on a raspberry Pi 4; and \n  2) to be able to use a variety of off-the-shelf actuating and sensing devices.\n\nFor information on setting up an SD card that will run this code on a Pi 4 with the correct\nconfiguration and attached devices, please [go here](docs/setting-up-the-pi.md)\n\n\n## SCADA Dev Environment Setup Guide\n\nThis is a setup guide for running SCADA, SCADA2, test `Ltn` and `admin` locally.\n\nThe SCADA uses the [gwproactor](github.com/SmoothStoneComputing/gridworks-proactor) framework, which models communication as links (upstream and downstream) over **MQTT**.\nA SCADA instance expects two different MQTT brokers:\n  1. **local_mqtt** — for communicating with the downstream actor (e.g. SCADA2)\n  2. **gridworks_mqtt** — the upstream broker, normally the cloud RabbitMQ (with MQTT plugin)\n\nFor **development**, we recommend:\n\n  - **Use 1 local RabbitMQ (with mqtt plugin)** to serve as the _gridworks_mqtt_ broker.\n  - **Use 1 local Mosquitto broker**  to serve as the _local_mqtt_ broker.\n\nThis mirrors production behavior while staying easy to run locally.\n\n### 1. Set Up the Brokers\n\n**A. Upstream rabbit broker** \n\nUse our prepared RabbitMQ dev image: found at  [https://github.com/thegridelectric/gridworks-base?tab=readme-ov-file#dev-rabbit-broker](https://github.com/thegridelectric/gridworks-base?tab=readme-ov-file#dev-rabbit-broker). In `arm.yml` or `x86.yml` you will see something like this:\n\n```\nservices:\n  rabbit:\n    container_name: gw-dev-rabbit\n    image: \"jessmillar/dev-rabbit-arm:chaos__fee74a3__20250120\"\n    ports:\n      - 1885:1885\n      - 5672:5672\n      - 15672:15672\n      - 15674:15674\n    env_file: ./for_docker/dev_vhost.env\n    environment:\n      - RABBITMQ_USERNAME=smqPublic\n      - RABBITMQ_PASSWORD=smqPublic\n      - RABBITMQ_PLUGINS=rabbitmq_management,rabbitmq_stomp,rabbitmq_web_stomp,rabbitmq_mqtt\n\n```\nVerify the broker is running by visiting:\n\n👉 http://localhost:15672\n(default credentials: `smqPublic` / `smqPublic`)\n\n\nThree steps for getting the docker rabbit instance to enable mqtt:\n```\ndocker exec -it gw-dev-rabbit rabbitmq-plugins enable rabbitmq_mqtt\ndocker exec -it gw-dev-rabbit rabbitmqctl restart_app\ndocker exec -it gw-dev-rabbit rabbitmq-plugins list\n```\n\nAnd confirm:\n [E*] rabbitmq_mqtt                     3.9.13\n\n(Troubleshooting how to get this into the docker yaml file over in gridworks-base)\n\nTest mqtt access via mqtt_sub:\n\n```\nmosquitto_sub -h localhost -p 1885 -u smqPublic -P smqPublic -t \"#\" -v\n```\n\nShould see an `mqtt-subscription-XXX` show up on the rabbit admin pannel http://localhost:15672/#/queues.\n\n\n**B. Optional Local Mosquitto Broker (local_mqtt)**\n\nUse this if you want to also have a `SCADA2` in the dev environment. Follow the instructions here\n\n 🔗 [https://gridworks-proactor.readthedocs.io/en/latest/#mosquitto](https://gridworks-proactor.readthedocs.io/en/latest/#mosquitto) \n\nThis will be the \"LAN_side\" MQTT broker, used by SCADA with a downstream actor.  The Scada actor will still run if it cannot connect to this broker. \n\n### 2. Setting up the SCADA Dev Environment\n\n**A. Create the Python Virtual Environment**\n\n``` \n./tools/mkenv.sh\n```\nThis creates `gw_spaceheat/venv/` *(On a Pi this is `./tools/mkenv-pi.sh`)*.\n\n**B. Install the `gws` CLI*\u0026\n```\n./tools/install-gws.sh\n```\n\n**C. Set Your Python Path \u0026 Alias**\n\n\nExample alias (adjust paths as needed):\n\n```\nalias gw=\"source $HOME/Coding/gridworks-scada/gw_spaceheat/venv/bin/activate \\\n  \u0026\u0026 cd $HOME/Coding/gridworks-scada \\\n  \u0026\u0026 export PYTHONPATH=$HOME/Coding/gridworks-scada/gw_spaceheat:$PYTHONPATH\"\n```\n\n**D. Create your `.env`**\n\n```\ncp .env-template .env\n```\n\n\n### E. Run the SCADA\n\n```\ngws run\n```\n\nIf all is set correctly, you should a second `mqtt-subscription-XXX` queue on the admin panel, and also some messages go by on the mosquitto_sub window..\n\n\n### F. Run `Ltn` in repl\nIn a new terminal window:\n\n```\nfrom actors.ltn.config import LtnSettings; from ltn_app import LtnApp; import dotenv\nl = LtnApp.get_repl_app(app_settings=LtnSettings(_env_file=dotenv.find_dotenv())).ltn\n```\n\n### G. Run `SCADA` in repl\n\n```\nimport typing\nfrom scada_app import ScadaApp\nfrom actors.config import ScadaSettings\nimport dotenv\nef = dotenv.find_dotenv()\nsettings = ScadaSettings(_env_file=ef)\napp = typing.cast(ScadaApp, ScadaApp.make_app_for_cli(app_settings=settings, env_file=ef))\n\napp.run_in_thread()\n\n# the scada actor (in gw_spaceheat/actors/scada.py) is the scada proactor app's prime_actor\ns = app.prime_actor\n```\n\n\n### H. Creating your own dev hardware layout\n  \nIn a sibling directory clone the [tlayouts](https://github.com/thegridelectric/tlayouts) directory. Then: \n\n - While in the virtual env for this repository, navigate to `tlayouts.\n - Run `gen_orange.py`\n\nThis will generate a simulated hardware layout in `outputs/orange.generated.json` \n - Change your `.env` to include:\n\n```\nSCADA_PATHS__HARDWARE_LAYOUT=\"../tlayouts/output/orange.generated.json\"\nLTN_PATHS__HARDWARE_LAYOUT=\"../tlayouts/output/orange.generated.json\"\n```\n\n## Testing\nRun the tests from the root directory of the repo with:\n\n```shell\npytest\n```\n\nA hardware layout file is necessary to run the scada locally. Find the default path the layout file with: \n\n```shell\npython -c \"from gwproactor.config.paths import Paths; print(Paths().hardware_layout)\"\n```    \n\nFor initial experiments the test layout file can be used. The test layout file is located at:\n    \n    tests/config/hardware-layout.json\n\nDisplay the hardware layout with:\n\n```shell\ngws layout show\n```    \n\nDisplay current settings with: \n    \n```shell\ngws config\n```\n\nThere are some scratch notes on Pi-related setup (like enabling interfaces) in docs/pi_setup.md\n\n### Adding libraries \nAdd libraries by adding the library spec to the appropriate \".in\" file in the \n[requirements directory](./gw_spaceheat/requirements). Use:\n\n* [dev.in](./gw_spaceheat/requirements/dev.in) for requirements only needed for\n  development or CI. \n* [drivers.in](./gw_spaceheat/requirements/drivers.in) for requirements only\n  needed on a Pi. \n* [base.in](./gw_spaceheat/requirements/base.in) for requirements used used\n  in all contexts. \n\nOnce you have added your requirement run: \n\n    tools/pipc.sh\n\nThen **manually** modify the modified .txt files to remove the absolute paths\nthat pip-tools adds to comments inside the .txt files. This allow someone looking\nat the commit to see only the dependency you changed. Otherwise they will see a \nchange in the comment many dependencies. For example, in a text editor or IDE\ndo a search/replace in *.txt files in the project, searching for the text\n    \n    path/to/scada/repo/on/your/machine/gw_spaceheat\n\nand replacing it with\n    \n    gw_spaceheat\n\n### Handling secrets and config variables\n    \nSETTING UP SECRETS.\nConfiguration variables (secret or otherwise) use dotenv module in a gitignored `.env` file, copied over from `.env-template`. These are accessed via `config.ScadaSettings`.\n\n\n\n\n### Static analsyis with ruff\n\n[Ruff](https://docs.astral.sh/ruff/) is installed via the test and dev requirements. \nYou can run it with:\n\n```shell\nruff check\n```\n\nRuff is *not* run in CI or in pre-commit, since the code will not currently pass. \nRuff is provided primarily for visual feedback in the IDE. Ruff is configured\nin `pyproject.toml`.\n\n\n### Static analysis in Visual Studio Code\n\nVisual Studio Code will provide visual feedback on code that does not pass ruff.\n\nTo use this functionality, the [ruff plugin](https://github.com/astral-sh/ruff-vscode)\nfor Visual Studio code must be installed. We recommend: \n\n1. Installing the ruff extension. \n2. Disabling it. \n3. Enabling for workspaces in which you want to use it, such as this one. \n\n### More static analysis\n\nMore rigid ruff rules can be applied by modifying pyproject.toml. Gwproto, for\nexample, uses [many more rules](https://github.com/thegridelectric/gridworks-protocol/blob/fb7e1a3d17073aad647c223730c41495e6238fd8/pyproject.toml#L124).\n\nTypechecking feedback can be applied in the IDE by enabling Pylance type checking\ninside [vscode](vscode://settings/python.analysis.typeCheckingMode). Change that\nin *user* not workspace settings since much of the code will currently fail. \n\n#### TLS\n\nTLS is used by default. Follow [these instructions](https://gridworks-proactor.readthedocs.io/en/latest/#tls) to set up\na local self-signed Certificate Authority to create test certificates and to create certificates for the Mosquitto\nbroker. Note that [this section](https://gridworks-proactor.readthedocs.io/en/latest/#external-connections)\nis relevant if you will connect to the Mosquitto broker from a Raspberry PI.\n\n##### Create a certificate for the test Ltn\n\n```shell\ngwcert key add --certs-dir $HOME/.config/gridworks/ltn/certs scada_mqtt\ncp $HOME/.local/share/gridworks/ca/ca.crt $HOME/.config/gridworks/ltn/certs/scada_mqtt\n```\n\n##### Create a certificate for test Scada\n\n```shell\ngwcert key add --certs-dir $HOME/.config/gridworks/scada/certs gridworks_mqtt\ncp $HOME/.local/share/gridworks/ca/ca.crt $HOME/.config/gridworks/scada/certs/gridworks_mqtt                    \n```\n\n##### Test generated certificates\n\nIn one terminal run: \n```shell\n\nmosquitto_sub -h localhost -p 8883 -t foo \\\n     --cafile $HOME/.config/gridworks/ltn/certs/scada_mqtt/ca.crt \\\n     --cert $HOME/.config/gridworks/ltn/certs/scada_mqtt/scada_mqtt.crt \\\n     --key $HOME/.config/gridworks/ltn/certs/scada_mqtt/private/scada_mqtt.pem\n\n```\nIn another terminal run: \n```shell\nmosquitto_pub -h localhost -p 8883 -t foo -m '{\"bar\":1}' \\\n     --cafile $HOME/.config/gridworks/scada/certs/gridworks_mqtt/ca.crt \\\n     --cert $HOME/.config/gridworks/scada/certs/gridworks_mqtt/gridworks_mqtt.crt \\\n     --key $HOME/.config/gridworks/scada/certs/gridworks_mqtt/private/gridworks_mqtt.pem\n\n```\n\nVerify you see `{\"bar\":1}` in the first window. \n\n#### Configuring a Scada with keys that can be used with the GridWorks MQTT broker. \n\nUse [getkeys.py](https://github.com/thegridelectric/gridworks-scada/blob/main/gw_spaceheat/getkeys.py) to\ncreate and copy TLS to keys to a scada such that it can communicate with the actual GridWorks MQTT broker. For details\nrun: \n```shell\npython gw_spaceheat/getkeys.py --help\n```\n\nThe overview of this process is that you need: \n1. The ssh key for `certbot`.\n2. [rclone](https://rclone.org/install/) installed. \n3. An rclone remote configured for your scada. \n4. To construct the `getkeys.py` command line per its help. \n\n## Running the code\n\nThis command will show information about what scada would do if started locally: \n```shell\ngws run --dry-run  \n```\n\nThis command will will start the scada locally: \n```shell\ngws run\n```\n\nThese commands will start the local test Ltn:\n```shell\ngws ltn run\n```\n\n## Development flow\n\nDefault branch is dev. Make PRs to this branch for review from your code branch. Make bug changes directly to this branch.\nThe first 5 homes in Millinocket are designed for beta testing. The idea here is that they run on dev, and the larger\ngroup of houses run on main.\n\nThe main branch is protected - requires a pull request. Default pattern is PRs from dev to main.\nThis will also publish a new gridworks-scada-protcol package and a new grdiworks-admin package.\n\n## Packages\n\nMotivated by the need to make [gridworks-admin] installable without setting up the\nscada development environment, this repository contains a [packages](./packages)\ndirectory for code published on PyPI as separate [distribution packages]. The\nseparate distribution packages are managed as separate by python projects, with\ntheir own subdirectory, each containing its own [pyproject.toml]. Subproject\ndevelopment is done using [uv] and publication to PyPI happens [in CI] after \nmerging to the main branch. \n\nThere are two packages: \n1. [gridworks-admin], the admin user interface.\n2. [gridworks-scada-protocol], which contains protocol messages shared by both\n   Scada and admin. \n\nSee the [packages directory README.md](./packages/README.md) for more information.\n\n## Admin\n\nTo install admin as a tool separate from development environment: \n\n```shell\nuv tool install gridworks-admin\n```\n\nor\n\n```shell\npipx install gridworks-admin\n```\n\nTo run admin from the development environment: \n\n```shell\ngwa\n```\n\nTo publish a new version admin to PyPI [install uv], if necessary, and then:\n1. Update the version field in the [admin pyproject.toml], either using\n   the [uv version] command, for example:\n   ```shell\n   cd packages/gridworks-admin\n   uv version --bump patch\n   ```\n   or by manually modifying the pyproject.toml and then running `uv lock`.\n2. Merge to main. \n\nSee the [packages directory README.md](./packages/README.md) for more information.\n\n## License\n\nDistributed under the terms of the [MIT license](./LICENSE),\nthis repository is free and open source software.\n\n## Contributing\n\nContributions are very welcome.\nTo learn more, see the [Contributor Guide].\n\n\n[distribution packages]: https://packaging.python.org/en/latest/discussions/distribution-package-vs-import-package/#distribution-package-vs-import-package\n[uv]: https://docs.astral.sh/uv/\n[pyproject.toml]: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/\n[in CI]: ./.github/workflows/release.yml\n[gridworks-admin]: https://pypi.org/project/gridworks-admin/\n[gridworks-scada-protocol]: https://pypi.org/project/gridworks-scada-protocol/\n[install uv]: https://docs.astral.sh/uv/getting-started/installation/#standalone-installer\n[admin pyproject.toml]: ./packages/gridworks-admin/pyproject.toml\n[uv version]: https://docs.astral.sh/uv/guides/package/#updating-your-version","funding_links":[],"readme_doi_urls":[],"works":{},"citation_counts":{},"total_citations":0,"keywords_from_contributors":[],"project_url":"https://ost.ecosyste.ms/api/v1/projects/191736","html_url":"https://ost.ecosyste.ms/projects/191736"}