Closed nisupa135 closed 2 weeks ago
Thanks,
If everything is configured correctly, it should work regardless of which method was used to change the IHC module output status.
Can you post the relevant config sections for the outputs (for Home Assistant: command_lines.yaml
, main configuration.yaml
, for IHCclient: modules.yaml
, settings.yaml
)?
Thank you for replying, this is what i have in the files: command_lines.yaml
- switch:
name: 1_1
command_on: curl --silent 'http://192.168.1.110:8081/ihcrequest' --data-binary '{"type":"setOutput","moduleNumber":1,"ioNumber":1,"state":true}'
command_off: curl --silent 'http://192.168.1.110:8081/ihcrequest' --data-binary '{"type":"setOutput","moduleNumber":1,"ioNumber":1,"state":false}'
- switch:
name: 1_2
command_on: curl --silent 'http://192.168.1.110:8081/ihcrequest' --data-binary '{"type":"setOutput","moduleNumber":1,"ioNumber":2,"state":true}'
command_off: curl --silent 'http://192.168.1.110:8081/ihcrequest' --data-binary '{"type":"setOutput","moduleNumber":1,"ioNumber":2,"state":false}'
- switch:
name: 1_3
command_on: curl --silent 'http://192.168.1.110:8081/ihcrequest' --data-binary '{"type":"setOutput","moduleNumber":1,"ioNumber":3,"state":true}'
command_off: curl --silent 'http://192.168.1.110:8081/ihcrequest' --data-binary '{"type":"setOutput","moduleNumber":1,"ioNumber":3,"state":false}'
configuration.yaml
# Loads default set of integrations. Do not remove.
default_config:
# Load frontend themes from the themes folder
frontend:
themes: !include_dir_merge_named themes
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
command_line: !include command_lines.yaml
shell_command: !include shell_commands.yaml
homeassistant:
customize: !include customize.yaml
modules.yaml
#######################################
# outputs
#######################################
# IHC output module 1, output 1
# llum pilot sofa
- type: outputState
moduleNumber: 1
ioNumber: 1
entity: switch.1_1
# IHC output module 1, output 2
# llum zona estar
- type: outputState
moduleNumber: 1
ioNumber: 2
entity: switch.1_2
# IHC output module 1, output 3
# llum zona estar totes
- type: outputState
moduleNumber: 1
ioNumber: 3
entity: switch.1_3
settings.yaml
# yaml configuration file for ihcclient
# IHCServer settings
ihc:
server: 192.168.1.110
port: 8081
username:
password:
# HASS server settings
hass:
server: 192.168.1.111
port: 8123
token: xxxx (i have the real token here)
# logfile name:
log_file: /var/log/ihc.log
Let's see if we're lucky and can find what's wrong.
No errors detected here.
So, if you switch the IHC output from HA, it switches correctly but the HA entity does not indicate status change?
Maybe try to add the customize.yaml
as described in my guide and set the assumed_state: false
for the switches? Also take a look at the log file and try to debug with the wscat
utility.
Side tip - please use the triple backticks to include code listings for readability.
Did you solve this? Also worth checking if your hass token in settings.yaml
is of correct length (should be 183 characters) and surrounded by single quotes ('
).
Hello,
I hadn't been home, so I wasn't able to test it before. After checking everything and adding the customize.yaml, it still wasn't working. My programming knowledge is limited, and using ChatGPT, it recommended adding the following modification to the ihcclient.py, and now it works perfectly.
def setHAState(entity, state):
# Note that we must first get the attributes, otherwise they will get lost
# if we later post only the state of the entity!
response = session.get('http://' + hass_server + '/api/states/' + entity, headers=hass_headers, verify=False)
json_data = response.json()
print(json_data) # For debugging
if 'attributes' in json_data:
attributes = json_data['attributes']
# Now we can post state AND attributes:
session.post('http://' + hass_server + '/api/states/' + entity, json={'attributes': attributes, 'state': state}, headers=hass_headers, verify=False)
logging.info('%s - %s %s', attributes['friendly_name'], entity, state)
else:
print(f"No attributes found in the state for entity {entity}")
logging.warning('No attributes found for entity %s', entity)
I don't quite understand why this modification makes it work, but it truly does. Maybe you can identify the reason.
This is the complete code of ihcclient.py:
#!/usr/bin/env python3
# listens to IHC events and updates the HA switch and sensor states accordingly
import sys
import logging
import json
import yaml
import requests
import websocket
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def readYamlConfig(configfile):
try:
with open(configfile, 'r') as file:
config_data = yaml.safe_load(file)
return config_data
except FileNotFoundError:
sys.exit("Error: file '" + configfile + "' not found")
except yaml.YAMLError as error:
sys.exit(error)
return None
# read main configuration file:
settings = readYamlConfig('settings.yaml')
ihc_server = settings['ihc']['server'] + ":" + str(settings['ihc']['port'])
ihc_username = settings['ihc']['username']
ihc_password = settings['ihc']['password']
hass_server = settings['hass']['server'] + ":" + str(settings['hass']['port'])
hass_token = settings['hass']['token']
log_file = settings['log_file']
# read IHC I/O configuration file:
pins = readYamlConfig('modules.yaml')
# set headers for HASS instance:
hass_headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + hass_token
}
# map IHC states to HASS states:
states = {
'True': 'on',
'False': 'off'
}
# set up logging:
logging.basicConfig(filename=log_file,
format='%(asctime)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=logging.INFO)
console = logging.StreamHandler()
logging.getLogger('').addHandler(console)
def setHAState(entity, state):
# Note that we must first get the attributes, otherwise they will get lost
# if we later post only the state of the entity!
response = session.get('http://' + hass_server + '/api/states/' + entity, headers=hass_headers, verify=False)
json_data = response.json()
print(json_data) # For debugging
if 'attributes' in json_data:
attributes = json_data['attributes']
# Now we can post state AND attributes:
session.post('http://' + hass_server + '/api/states/' + entity, json={'attributes': attributes, 'state': state}, headers=hass_headers, verify=False)
logging.info('%s - %s %s', attributes['friendly_name'], entity, state)
else:
print(f"No attributes found in the state for entity {entity}")
logging.warning('No attributes found for entity %s', entity)
def getIHCStates(moduleType):
for module in initial_states['modules'][moduleType + 'Modules']:
ioType = moduleType + 'State'
if module['state'] is True:
moduleNumber = module['moduleNumber']
for state in module[moduleType + 'States']:
ioNumber = state[moduleType + 'Number']
pin = next((e for e in pins if (
e['type'] == ioType and
e['moduleNumber'] == moduleNumber and
e['ioNumber'] == ioNumber)
), False)
if pin:
entity = pin['entity']
state = states.get(str(state[moduleType + 'State']))
setHAState(entity, state)
def createHTTPSession():
session = requests.Session()
retries = Retry(total=5, backoff_factor=1, status_forcelist=[502, 503, 504])
session.mount('http://', HTTPAdapter(max_retries=retries))
return session
session = createHTTPSession()
# read initial states from IHC controller:
print("Reading initial IHC states...")
response = session.post('http://' + ihc_server + '/ihcrequest', json={'type': 'getAll'}, auth=(ihc_username, ihc_password))
initial_states = response.json()
# print(initial_states)
getIHCStates('input')
getIHCStates('output')
# wait for events over websocket
ws = websocket.create_connection('ws://' + ihc_server + '/ihcevents-ws')
print("Waiting for IHC events...")
try:
while True:
event = ws.recv()
json_data = json.loads(event)
# print("Received", json_data)
ioType = json_data['type']
if ioType == 'ping':
ws.send(json.dumps({'type': 'pong'}))
continue
moduleNumber = json_data['moduleNumber']
ioNumber = json_data['ioNumber']
ioState = json_data['state']
state = states.get(str(ioState))
pin = next((e for e in pins if (
e['type'] == ioType and
e['moduleNumber'] == moduleNumber and
e['ioNumber'] == ioNumber)
), False)
if pin:
entity = pin['entity']
setHAState(entity, state)
except KeyboardInterrupt:
print("\nClosing connection...")
except Exception as error:
print("Error in WebSocket connection:", error)
finally:
ws.close()
print("Connection closed.")
The modification suggested by ChatGPT does not make much sense to me.
It basically checks if the entity has any attributes defined before it updates the status. But when the binary sensors and command line switches have already been defined in the configuration files, they will always have attributes (at least the friendly_name
which defaults to entity name).
I experimented with the current code today, adding some new IHC outputs without customize.yaml
settings and everything worked as expected. The key is to restart both the HA and the IHCClient after modifications, so HA will know about the new entities.
Glad You got it working, I think the HA just needed a restart.
Your comments are welcome and please let me know if I may close the issue. Or You can do it yourself.
Thank you very much for your work. I was able to integrate my old system, and it works correctly in Home Assistant. The only problem is that I cannot see the status of the outputs; they do not update. If I action the output from Home Assistant, it works, but if, for example, a light is turned on or off with a switch or another system, it does not show as on. Is there a solution? On the IHCServer Webinterface, the status of the output is visible and changes correctly.