Skip to content

Commit

Permalink
First version
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasmunoz13 committed Jun 29, 2024
1 parent fef9859 commit 42c3269
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 120 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/integration_tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Validate with HACS

on:
push:
pull_request:

jobs:
hassfest:
runs-on: 'ubuntu-latest'
steps:
- uses: 'actions/checkout@v4'
- uses: home-assistant/actions/hassfest@master
46 changes: 46 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Publish Workflow

on:
release:
types:
- published
push:
branches:
- master

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- name: Get integration information
id: information
run: |
name=$(find custom_components/ -type d -maxdepth 1 | tail -n 1 | cut -d "/" -f2)
echo "name=$name" >> $GITHUB_OUTPUT
- name: Adjust version number
if: ${{ github.event_name == 'release' }}
shell: bash
run: |
yq -i -o json '.version="${{ github.event.release.tag_name }}"' \
"${{ github.workspace }}/custom_components/${{ steps.information.outputs.name }}/manifest.json"
- name: Create zip file for the integration
shell: bash
run: |
cd "${{ github.workspace }}/custom_components/${{ steps.information.outputs.name }}"
zip ${{ steps.information.outputs.name }}.zip -r ./
- name: 📤 Upload zip to action
uses: actions/upload-artifact@v3.1.2
if: ${{ github.event_name == 'push' }}
with:
name: hcs_shutter_ha
path: ${{ github.workspace }}/custom_components/${{ steps.information.outputs.name }}
retention-days: 14
- name: Upload the zipfile as a release asset
uses: softprops/action-gh-release@v2
if: ${{ github.event_name == 'release' }}
with:
files: ${{ github.workspace }}/custom_components/${{ steps.information.outputs.name }}/${{ steps.information.outputs.name }}.zip
tag_name: ${{ github.event.release.tag_name }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ __pycache__/
Thumbs.db

# Ignore editor backup files
*~
*~
homeassistant/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ To install this integration, follow these steps:
4. Add the following configuration to your `configuration.yaml` file:

```yaml
shutter_roller:
hcs_shutter_ha:
covers:
- name: Foo
host: 192.168.1.100
Expand Down
45 changes: 45 additions & 0 deletions custom_components/hcs_shutter_ha/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import logging
from homeassistant.core import HomeAssistant
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.const import CONF_NAME, CONF_HOST
from homeassistant.config_entries import ConfigEntry
from .const import DOMAIN, CONF_COVERS

_LOGGER = logging.getLogger(__name__)

async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Shutter Roller component from YAML configuration."""
if DOMAIN not in config:
return True

conf = config[DOMAIN]
covers = conf.get(CONF_COVERS, [])

for cover in covers:
name = cover.get(CONF_NAME)
host = cover.get(CONF_HOST)

hass.data.setdefault(DOMAIN, {}).setdefault(name, {"host": host})

await async_load_platform(hass, "cover", DOMAIN, {"name": name, "host": host}, config)

return True

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Shutter Roller from a config entry."""
name = entry.data[CONF_NAME]
host = entry.data[CONF_HOST]

hass.data.setdefault(DOMAIN, {}).setdefault(name, {"host": host})

await hass.config_entries.async_forward_entry_setup(entry, "cover")

return True

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
await hass.config_entries.async_forward_entry_unload(entry, "cover")

hass.data[DOMAIN].pop(entry.data[CONF_NAME])

return True
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.core import callback
from .const import DOMAIN, CONF_NAME, CONF_HOST
from .const import DOMAIN, CONF_HOST

@callback
def configured_instances(hass):
Expand All @@ -12,11 +12,12 @@ class ShutterRollerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):

async def async_step_user(self, user_input=None):
if user_input is not None:
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
return self.async_create_entry(title=user_input['name'], data=user_input)

data_schema = vol.Schema({
vol.Required(CONF_NAME): str,
vol.Required('name'): str,
vol.Required(CONF_HOST): str,
vol.Required('shutter_id'): str,
})

return self.async_show_form(step_id="user", data_schema=data_schema)
7 changes: 7 additions & 0 deletions custom_components/hcs_shutter_ha/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DOMAIN = "hcs_shutter_ha"
CONF_COVERS = "covers"
CONF_HOST = "host"
CONF_NAME = "name"
MANUFACTURER = "HCS"
MODEL = "Shutter Roller"
SW_VERSION = "1.0.0"
123 changes: 123 additions & 0 deletions custom_components/hcs_shutter_ha/cover.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import requests
import logging
from homeassistant.components.cover import CoverEntity
from homeassistant.const import CONF_NAME, CONF_HOST
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from .const import DOMAIN, MANUFACTURER, MODEL, SW_VERSION

_LOGGER = logging.getLogger(__name__)

async def async_setup_platform(hass: HomeAssistant, config: dict, async_add_entities: AddEntitiesCallback, discovery_info=None):
"""Set up the Shutter Roller platform from YAML configuration."""
if discovery_info is None:
return

name = discovery_info[CONF_NAME]
host = discovery_info[CONF_HOST]
shutter_id = discovery_info['shutter_id']
_LOGGER.debug(f"Setting up Shutter Roller cover: {name} with host: {host}")

async_add_entities([ShutterRollerCover(hass, name, host, shutter_id)])

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback):
"""Set up Shutter Roller cover from a config entry."""
name = entry.data['name']
host = entry.data[CONF_HOST]
shutter_id = entry.data['shutter_id']

async_add_entities([ShutterRollerCover(hass, name, host, shutter_id)])

class ShutterRollerAPI:
def __init__(self, base_url, shutter_id, logger):
self.base_url = f"{base_url}/shutter/{shutter_id}"
self.logger = logger

def get_position(self):
try:
response = requests.get(self.base_url, timeout=60)
response.raise_for_status()
self.logger.info(response.json())
return response.json()['data']['currentOpenRatio'] * 100
except requests.exceptions.RequestException as error:
self.logger.error(f"Error getting position: {error}")
return -1

def set_position(self, value):
try:
response = requests.get(f"{self.base_url}/ratio?arg={value / 100.0}", timeout=60)
response.raise_for_status()
self.logger.info(response)
return True
except requests.exceptions.RequestException as error:
self.logger.error(f"Error setting position: {error}")
return False

class ShutterRollerCover(CoverEntity):
def __init__(self, hass: HomeAssistant, name, host, shutter_id):
self.hass = hass
self._name = name
self._host = host
self._shutter_id = shutter_id
self._position = 0
self._is_closed = True
self.api = ShutterRollerAPI(host, shutter_id, _LOGGER)

@property
def name(self):
return self._name

@property
def is_closed(self):
return self._is_closed

@property
def current_cover_position(self):
return self._position

@property
def unique_id(self):
return f"{self._host}_{self._shutter_id}"

async def async_close_cover(self, **kwargs):
result = await self.hass.async_add_executor_job(self.api.set_position, 0)
if result:
self._is_closed = True
self._position = 0
self.async_schedule_update_ha_state()

async def async_open_cover(self, **kwargs):
result = await self.hass.async_add_executor_job(self.api.set_position, 100)
if result:
self._is_closed = False
self._position = 100
self.async_schedule_update_ha_state()

async def async_set_cover_position(self, **kwargs):
position = kwargs.get('position', 0)
result = await self.hass.async_add_executor_job(self.api.set_position, position)
if result:
self._position = position
self._is_closed = position == 0
self.async_schedule_update_ha_state()

async def async_update(self):
position = await self.hass.async_add_executor_job(self.api.get_position)
if position != -1:
self._position = position
self._is_closed = position == 0
self.async_schedule_update_ha_state()

@property
def device_info(self) -> DeviceInfo:
"""Return device information about this cover."""
return DeviceInfo(
identifiers={(DOMAIN, self._shutter_id)},
name=self._name,
manufacturer=MANUFACTURER,
model=MODEL,
sw_version=SW_VERSION,
entry_type=DeviceEntryType.SERVICE
)
20 changes: 20 additions & 0 deletions custom_components/hcs_shutter_ha/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"domain": "hcs_shutter_ha",
"name": "HCS Shutter Roller",
"codeowners": [
"@thomasmunoz13"
],
"config_flow": true,
"dependencies": [],
"documentation": "https://github.com/thomasmunoz13/hcs_shutter_ha",
"integration_type": "hub",
"iot_class": "local_polling",
"issue_tracker": "https://github.com/thomasmunoz13/hcs_shutter_ha/issues",
"loggers": [
"custom_components.mbapi2020"
],
"requirements": [
"requests"
],
"version": "1.0.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"description": "Set up your Shutter Roller cover.",
"data": {
"name": "Name",
"host": "Host"
"host": "Host",
"shutter_id": "Shutter ID"
}
}
}
Expand All @@ -19,7 +20,8 @@
"description": "Configure the Shutter Roller cover options.",
"data": {
"name": "Name",
"host": "Host"
"host": "Host",
"shutter_id": "Shutter ID"
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions custom_components/hcs_shutter_ha/translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"title": "Shutter Roller",
"config": {
"step": {
"user": {
"title": "Configure Shutter Roller",
"description": "Set up your Shutter Roller cover.",
"data": {
"name": "Name",
"host": "Host",
"shutter_id": "Shutter ID"
}
}
}
},
"options": {
"step": {
"init": {
"title": "Configure Shutter Roller",
"description": "Configure the Shutter Roller cover options.",
"data": {
"name": "Name",
"host": "Host",
"shutter_id": "Shutter ID"
}
}
}
},
"services": {
"set_cover_position": {
"name": "Set Cover Position",
"description": "Set the position of the cover.",
"fields": {
"position": {
"name": "Position",
"description": "The position to set the cover to."
}
}
}
}
}
10 changes: 0 additions & 10 deletions custom_components/shutter_roller/__init__.py

This file was deleted.

4 changes: 0 additions & 4 deletions custom_components/shutter_roller/const.py

This file was deleted.

Loading

0 comments on commit 42c3269

Please sign in to comment.